diff --git a/ManagerService/Controllers/ApplicationInstanceController.cs b/ManagerService/Controllers/ApplicationInstanceController.cs
index 32bc4de..46f114d 100644
--- a/ManagerService/Controllers/ApplicationInstanceController.cs
+++ b/ManagerService/Controllers/ApplicationInstanceController.cs
@@ -36,6 +36,9 @@ namespace ManagerService.Controllers
_myInfoMateDbContext = myInfoMateDbContext;
}
+ private bool IsSuperAdmin() =>
+ User.HasClaim(ManagerService.Service.Security.ClaimTypes.Permission, ManagerService.Service.Security.Permissions.SuperAdmin);
+
///
/// Get a list of all applicationInstance (summary)
///
@@ -54,6 +57,11 @@ namespace ManagerService.Controllers
return new OkObjectResult(applicationInstances.Select(ai => {
var dto = ai.ToDTO(_myInfoMateDbContext);
dto.hasStats = instance?.HasStats ?? false;
+ // Porté par l'Instance, pas par l'application. La page /download de
+ // visitapp-web n'a que l'instanceId d'un QR imprimé : sans ce slug, elle
+ // ne peut pas renvoyer vers la visite web d'une instance sans app mobile.
+ dto.webSlug = instance?.WebSlug;
+ dto.instanceName = instance?.Name;
return dto;
}).OrderBy(c => c.appType));
}
@@ -92,6 +100,12 @@ namespace ManagerService.Controllers
ApplicationInstance applicationInstance = new ApplicationInstance().FromDTO(newApplicationInstanceDTO);
applicationInstance.Id = idService.GenerateHexId();
+ if (!IsSuperAdmin())
+ {
+ applicationInstance.AppStoreUrl = null;
+ applicationInstance.PlayStoreUrl = null;
+ }
+
_myInfoMateDbContext.ApplicationInstances.Add(applicationInstance);
_myInfoMateDbContext.SaveChanges();
@@ -133,7 +147,17 @@ namespace ManagerService.Controllers
if (applicationInstance == null)
throw new KeyNotFoundException("application instance does not exist");
+ var appStoreUrl = applicationInstance.AppStoreUrl;
+ var playStoreUrl = applicationInstance.PlayStoreUrl;
+
applicationInstance = applicationInstance.FromDTO(updatedApplicationInstanceDTO);
+
+ if (!IsSuperAdmin())
+ {
+ applicationInstance.AppStoreUrl = appStoreUrl;
+ applicationInstance.PlayStoreUrl = playStoreUrl;
+ }
+
_myInfoMateDbContext.SaveChanges();
return new OkObjectResult(applicationInstance.ToDTO(_myInfoMateDbContext));
diff --git a/ManagerService/Controllers/OnboardingController.cs b/ManagerService/Controllers/OnboardingController.cs
index 54548b6..1addead 100644
--- a/ManagerService/Controllers/OnboardingController.cs
+++ b/ManagerService/Controllers/OnboardingController.cs
@@ -65,7 +65,7 @@ namespace ManagerService.Controllers
public ObjectResult CheckSlug(string slug)
{
var sanitized = SanitizeSlug(slug);
- var available = sanitized.Length >= 3 && !_myInfoMateDbContext.Instances.Any(i => i.WebSlug == sanitized);
+ var available = sanitized.Length >= 3 && !SlugHelper.IsTaken(_myInfoMateDbContext, sanitized);
return new OkObjectResult(new { slug = sanitized, available });
}
@@ -176,7 +176,7 @@ namespace ManagerService.Controllers
var slug = SanitizeSlug(dto.slug);
if (slug.Length < 3)
throw new ArgumentNullException(nameof(dto.slug), "Slug must be at least 3 characters");
- if (_myInfoMateDbContext.Instances.Any(i => i.WebSlug == slug))
+ if (SlugHelper.IsTaken(_myInfoMateDbContext, slug))
throw new InvalidOperationException("This address is already taken");
var plan = _myInfoMateDbContext.SubscriptionPlans.FirstOrDefault(p => p.Id == EssentielPlanId);
diff --git a/ManagerService/DTOs/ApplicationInstanceDTO.cs b/ManagerService/DTOs/ApplicationInstanceDTO.cs
index 48a9210..e516f5d 100644
--- a/ManagerService/DTOs/ApplicationInstanceDTO.cs
+++ b/ManagerService/DTOs/ApplicationInstanceDTO.cs
@@ -35,5 +35,19 @@ namespace ManagerService.DTOs
public bool isAssistant { get; set; }
public bool hasStats { get; set; }
+
+ /// Slug web de l'Instance. Lecture seule, rempli par le contrôleur.
+ public string? webSlug { get; set; }
+
+ /// Nom de l'Instance. Lecture seule, rempli par le contrôleur.
+ public string? instanceName { get; set; }
+
+ public bool isQRCodeEnabled { get; set; } = true;
+
+ public List? appName { get; set; }
+
+ public string? appStoreUrl { get; set; }
+
+ public string? playStoreUrl { get; set; }
}
}
diff --git a/ManagerService/Data/ApplicationInstance.cs b/ManagerService/Data/ApplicationInstance.cs
index fa0c592..e9afc93 100644
--- a/ManagerService/Data/ApplicationInstance.cs
+++ b/ManagerService/Data/ApplicationInstance.cs
@@ -54,6 +54,14 @@ namespace ManagerService.Data
public bool IsAssistant { get; set; } = false;
+ public bool IsQRCodeEnabled { get; set; } = true;
+
+ public List? AppName { get; set; } // Specific Mobile et web
+
+ // Chaque client a sa propre app publiée par Unov : seul un SuperAdmin les modifie.
+ public string? AppStoreUrl { get; set; } // Specific Mobile
+
+ public string? PlayStoreUrl { get; set; } // Specific Mobile
public ApplicationInstanceDTO ToDTO(MyInfoMateDbContext myInfoMateDbContext)
{
@@ -80,7 +88,11 @@ namespace ManagerService.Data
languages = Languages,
sectionEventId = SectionEventId,
sectionEventDTO = sectionEventDTO,
- isAssistant = IsAssistant
+ isAssistant = IsAssistant,
+ isQRCodeEnabled = IsQRCodeEnabled,
+ appName = AppName,
+ appStoreUrl = AppStoreUrl,
+ playStoreUrl = PlayStoreUrl
};
}
@@ -98,6 +110,10 @@ namespace ManagerService.Data
Configurations = dto.configurations;
SectionEventId = dto.sectionEventId;
IsAssistant = dto.isAssistant;
+ IsQRCodeEnabled = dto.isQRCodeEnabled;
+ AppName = dto.appName;
+ AppStoreUrl = dto.appStoreUrl;
+ PlayStoreUrl = dto.playStoreUrl;
return this;
}
diff --git a/ManagerService/Data/MyInfoMateDbContext.cs b/ManagerService/Data/MyInfoMateDbContext.cs
index da5b060..4e508b1 100644
--- a/ManagerService/Data/MyInfoMateDbContext.cs
+++ b/ManagerService/Data/MyInfoMateDbContext.cs
@@ -265,6 +265,13 @@ namespace ManagerService.Data
v => JsonSerializer.Serialize(v, options),
v => JsonSerializer.Deserialize>(v, options));
+ modelBuilder.Entity()
+ .Property(ai => ai.AppName)
+ .HasColumnType("jsonb")
+ .HasConversion(
+ v => JsonSerializer.Serialize(v, options),
+ v => JsonSerializer.Deserialize>(v, options));
+
// Guide IA — recherche sémantique.
// Le type vector n'existe que sur Npgsql : les tests tournent sur EF InMemory,
// qui échoue à la validation du modèle s'il rencontre une propriété Vector.
diff --git a/ManagerService/Helpers/SlugHelper.cs b/ManagerService/Helpers/SlugHelper.cs
index fb58ace..772749d 100644
--- a/ManagerService/Helpers/SlugHelper.cs
+++ b/ManagerService/Helpers/SlugHelper.cs
@@ -6,6 +6,15 @@ namespace ManagerService.Helpers
{
public static class SlugHelper
{
+ ///
+ /// Premiers segments d'URL déjà pris par des routes de visitapp-web : un segment
+ /// statique l'emporte sur [slug], l'instance serait donc inaccessible.
+ ///
+ private static readonly string[] ReservedSlugs = { "download", "demo", "api" };
+
+ public static bool IsTaken(MyInfoMateDbContext db, string slug) =>
+ ReservedSlugs.Contains(slug) || db.Instances.Any(i => i.WebSlug == slug);
+
///
/// Generate a URL-safe slug from a name, appending "-1", "-2", etc. until unique among Instances.WebSlug.
///
@@ -24,7 +33,7 @@ namespace ManagerService.Helpers
var baseSlug = slug;
var counter = 1;
- while (db.Instances.Any(i => i.WebSlug == slug))
+ while (IsTaken(db, slug))
slug = $"{baseSlug}-{counter++}";
return slug;
diff --git a/ManagerService/Migrations/20260910083904_AddAppNameQrAndStoresToApplicationInstance.Designer.cs b/ManagerService/Migrations/20260910083904_AddAppNameQrAndStoresToApplicationInstance.Designer.cs
new file mode 100644
index 0000000..521faa4
--- /dev/null
+++ b/ManagerService/Migrations/20260910083904_AddAppNameQrAndStoresToApplicationInstance.Designer.cs
@@ -0,0 +1,1934 @@
+//
+using System;
+using System.Collections.Generic;
+using Manager.DTOs;
+using ManagerService.DTOs;
+using ManagerService.Data;
+using Microsoft.EntityFrameworkCore;
+using Microsoft.EntityFrameworkCore.Infrastructure;
+using Microsoft.EntityFrameworkCore.Migrations;
+using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
+using NetTopologySuite.Geometries;
+using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata;
+using Pgvector;
+
+#nullable disable
+
+namespace ManagerService.Migrations
+{
+ [DbContext(typeof(MyInfoMateDbContext))]
+ [Migration("20260910083904_AddAppNameQrAndStoresToApplicationInstance")]
+ partial class AddAppNameQrAndStoresToApplicationInstance
+ {
+ ///
+ protected override void BuildTargetModel(ModelBuilder modelBuilder)
+ {
+#pragma warning disable 612, 618
+ modelBuilder
+ .HasAnnotation("ProductVersion", "9.0.2")
+ .HasAnnotation("Relational:MaxIdentifierLength", 63);
+
+ NpgsqlModelBuilderExtensions.HasPostgresExtension(modelBuilder, "postgis");
+ NpgsqlModelBuilderExtensions.HasPostgresExtension(modelBuilder, "vector");
+ NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder);
+
+ modelBuilder.Entity("ManagerService.Data.ApiKey", b =>
+ {
+ b.Property("Id")
+ .HasColumnType("text");
+
+ b.Property("AppType")
+ .HasColumnType("integer");
+
+ b.Property("DateCreation")
+ .HasColumnType("timestamp with time zone");
+
+ b.Property("DateExpiration")
+ .HasColumnType("timestamp with time zone");
+
+ b.Property("DateUpdate")
+ .HasColumnType("timestamp with time zone");
+
+ b.Property("InstanceId")
+ .IsRequired()
+ .HasColumnType("text");
+
+ b.Property("IsActive")
+ .HasColumnType("boolean");
+
+ b.Property("Key")
+ .HasColumnType("text");
+
+ b.Property("KeyHash")
+ .HasColumnType("text");
+
+ b.Property("Name")
+ .IsRequired()
+ .HasColumnType("text");
+
+ b.HasKey("Id");
+
+ b.HasIndex("InstanceId");
+
+ b.ToTable("ApiKeys");
+ });
+
+ modelBuilder.Entity("ManagerService.Data.AppConfigurationLink", b =>
+ {
+ b.Property("Id")
+ .HasColumnType("text");
+
+ b.Property("ApplicationInstanceId")
+ .IsRequired()
+ .HasColumnType("text");
+
+ b.Property("ConfigurationId")
+ .IsRequired()
+ .HasColumnType("text");
+
+ b.Property("DateCreation")
+ .HasColumnType("timestamp with time zone");
+
+ b.Property("DateUpdate")
+ .HasColumnType("timestamp with time zone");
+
+ b.Property("DeviceId")
+ .HasColumnType("text");
+
+ b.Property("GridColSpan")
+ .HasColumnType("integer");
+
+ b.Property("GridRowSpan")
+ .HasColumnType("integer");
+
+ b.Property("IsActive")
+ .HasColumnType("boolean");
+
+ b.Property("IsDate")
+ .HasColumnType("boolean");
+
+ b.Property("IsHour")
+ .HasColumnType("boolean");
+
+ b.Property("IsSectionImageBackground")
+ .HasColumnType("boolean");
+
+ b.Property("LoaderImageId")
+ .HasColumnType("text");
+
+ b.Property("LoaderImageUrl")
+ .HasColumnType("text");
+
+ b.Property("Order")
+ .HasColumnType("integer");
+
+ b.Property("PrimaryColor")
+ .HasColumnType("text");
+
+ b.Property("RoundedValue")
+ .HasColumnType("integer");
+
+ b.Property("ScreenPercentageSectionsMainPage")
+ .HasColumnType("integer");
+
+ b.Property("SecondaryColor")
+ .HasColumnType("text");
+
+ b.HasKey("Id");
+
+ b.HasIndex("ApplicationInstanceId");
+
+ b.HasIndex("ConfigurationId");
+
+ b.HasIndex("DeviceId");
+
+ b.ToTable("AppConfigurationLinks");
+ });
+
+ modelBuilder.Entity("ManagerService.Data.ApplicationInstance", b =>
+ {
+ b.Property("Id")
+ .HasColumnType("text");
+
+ b.Property("AppName")
+ .HasColumnType("jsonb");
+
+ b.Property("AppStoreUrl")
+ .HasColumnType("text");
+
+ b.Property("AppType")
+ .HasColumnType("integer");
+
+ b.Property("DateCreation")
+ .HasColumnType("timestamp with time zone");
+
+ b.Property("DateUpdate")
+ .HasColumnType("timestamp with time zone");
+
+ b.Property("InstanceId")
+ .IsRequired()
+ .HasColumnType("text");
+
+ b.Property("IsAssistant")
+ .HasColumnType("boolean");
+
+ b.Property("IsQRCodeEnabled")
+ .HasColumnType("boolean");
+
+ b.PrimitiveCollection>("Languages")
+ .HasColumnType("text[]");
+
+ b.Property("LoaderImageId")
+ .HasColumnType("text");
+
+ b.Property("LoaderImageUrl")
+ .HasColumnType("text");
+
+ b.Property("MainImageId")
+ .HasColumnType("text");
+
+ b.Property("MainImageUrl")
+ .HasColumnType("text");
+
+ b.Property("PlayStoreUrl")
+ .HasColumnType("text");
+
+ b.Property("PrimaryColor")
+ .HasColumnType("text");
+
+ b.Property("SecondaryColor")
+ .HasColumnType("text");
+
+ b.Property("SectionEventId")
+ .HasColumnType("text");
+
+ b.HasKey("Id");
+
+ b.HasIndex("InstanceId");
+
+ b.HasIndex("SectionEventId");
+
+ b.HasIndex("InstanceId", "AppType")
+ .IsUnique();
+
+ b.ToTable("ApplicationInstances");
+ });
+
+ modelBuilder.Entity("ManagerService.Data.AuditLog", b =>
+ {
+ b.Property("Id")
+ .HasColumnType("text");
+
+ b.Property("Action")
+ .HasColumnType("text");
+
+ b.Property("EntityId")
+ .HasColumnType("text");
+
+ b.Property("EntityType")
+ .HasColumnType("text");
+
+ b.Property("InstanceId")
+ .HasColumnType("text");
+
+ b.Property("NewValues")
+ .HasColumnType("text");
+
+ b.Property("OldValues")
+ .HasColumnType("text");
+
+ b.Property("Timestamp")
+ .HasColumnType("timestamp with time zone");
+
+ b.Property("UserId")
+ .HasColumnType("text");
+
+ b.HasKey("Id");
+
+ b.HasIndex("InstanceId");
+
+ b.ToTable("AuditLogs");
+ });
+
+ modelBuilder.Entity("ManagerService.Data.Configuration", b =>
+ {
+ b.Property("Id")
+ .HasColumnType("text");
+
+ b.Property("DateCreation")
+ .HasColumnType("timestamp with time zone");
+
+ b.Property("DateUpdate")
+ .HasColumnType("timestamp with time zone");
+
+ b.Property("ImageId")
+ .HasColumnType("text");
+
+ b.Property("ImageSource")
+ .HasColumnType("text");
+
+ b.Property("InstanceId")
+ .IsRequired()
+ .HasColumnType("text");
+
+ b.Property("IsOffline")
+ .HasColumnType("boolean");
+
+ b.Property("IsQRCode")
+ .HasColumnType("boolean");
+
+ b.Property("IsSearchNumber")
+ .HasColumnType("boolean");
+
+ b.Property("IsSearchText")
+ .HasColumnType("boolean");
+
+ b.Property("Label")
+ .IsRequired()
+ .HasColumnType("text");
+
+ b.PrimitiveCollection>("Languages")
+ .HasColumnType("text[]");
+
+ b.Property("LoaderImageId")
+ .HasColumnType("text");
+
+ b.Property("LoaderImageUrl")
+ .HasColumnType("text");
+
+ b.Property("PrimaryColor")
+ .HasColumnType("text");
+
+ b.Property("SecondaryColor")
+ .HasColumnType("text");
+
+ b.Property("Title")
+ .IsRequired()
+ .HasColumnType("jsonb");
+
+ b.HasKey("Id");
+
+ b.HasIndex("InstanceId");
+
+ b.ToTable("Configurations");
+ });
+
+ modelBuilder.Entity("ManagerService.Data.ContentEmbedding", b =>
+ {
+ b.Property("Id")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("bigint");
+
+ NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id"));
+
+ b.Property("ChunkIndex")
+ .HasColumnType("integer");
+
+ b.Property("ConfigurationId")
+ .HasColumnType("text");
+
+ b.Property("ContentId")
+ .IsRequired()
+ .HasColumnType("text");
+
+ b.Property("ContentType")
+ .HasColumnType("integer");
+
+ b.Property("Embedding")
+ .IsRequired()
+ .HasColumnType("vector(768)");
+
+ b.Property("InstanceId")
+ .IsRequired()
+ .HasColumnType("text");
+
+ b.Property("Language")
+ .IsRequired()
+ .HasMaxLength(5)
+ .HasColumnType("character varying(5)");
+
+ b.Property("PageNumber")
+ .HasColumnType("integer");
+
+ b.Property("Text")
+ .IsRequired()
+ .HasColumnType("text");
+
+ b.Property("UpdatedAt")
+ .HasColumnType("timestamp with time zone");
+
+ b.HasKey("Id");
+
+ b.HasIndex("Embedding");
+
+ NpgsqlIndexBuilderExtensions.HasMethod(b.HasIndex("Embedding"), "hnsw");
+ NpgsqlIndexBuilderExtensions.HasOperators(b.HasIndex("Embedding"), new[] { "vector_cosine_ops" });
+
+ b.HasIndex("InstanceId", "ContentType");
+
+ b.HasIndex("ContentType", "ContentId", "ChunkIndex")
+ .IsUnique();
+
+ b.ToTable("ContentEmbeddings");
+ });
+
+ modelBuilder.Entity("ManagerService.Data.Device", b =>
+ {
+ b.Property("Id")
+ .HasColumnType("text");
+
+ b.Property("AppVersion")
+ .HasColumnType("text");
+
+ b.Property("BatteryLevel")
+ .HasColumnType("text");
+
+ b.Property("ConfigurationId")
+ .IsRequired()
+ .HasColumnType("text");
+
+ b.Property("Connected")
+ .HasColumnType("boolean");
+
+ b.Property("ConnectionLevel")
+ .HasColumnType("text");
+
+ b.Property("DateCreation")
+ .HasColumnType("timestamp with time zone");
+
+ b.Property("DateUpdate")
+ .HasColumnType("timestamp with time zone");
+
+ b.Property("Identifier")
+ .HasColumnType("text");
+
+ b.Property("InstanceId")
+ .IsRequired()
+ .HasColumnType("text");
+
+ b.Property("IpAddressETH")
+ .HasColumnType("text");
+
+ b.Property("IpAddressWLAN")
+ .HasColumnType("text");
+
+ b.Property("LastBatteryLevel")
+ .HasColumnType("timestamp with time zone");
+
+ b.Property("LastConnectionLevel")
+ .HasColumnType("timestamp with time zone");
+
+ b.Property("LastSeen")
+ .HasColumnType("timestamp with time zone");
+
+ b.Property("Name")
+ .HasColumnType("text");
+
+ b.HasKey("Id");
+
+ b.HasIndex("ConfigurationId");
+
+ b.HasIndex("InstanceId");
+
+ b.ToTable("Devices");
+ });
+
+ modelBuilder.Entity("ManagerService.Data.Instance", b =>
+ {
+ b.Property("Id")
+ .HasColumnType("text");
+
+ b.Property("AiTokensPerMonth")
+ .HasColumnType("bigint");
+
+ b.Property("AiTokensThisMonth")
+ .HasColumnType("bigint");
+
+ b.Property("AiUsageMonthKey")
+ .HasColumnType("text");
+
+ b.Property("BillingAddress")
+ .HasColumnType("text");
+
+ b.Property("BillingCountry")
+ .HasColumnType("text");
+
+ b.Property("DateCreation")
+ .HasColumnType("timestamp with time zone");
+
+ b.Property("DateUpdate")
+ .HasColumnType("timestamp with time zone");
+
+ b.Property("GuideFallbackMessages")
+ .HasColumnType("jsonb");
+
+ b.Property("GuideName")
+ .HasColumnType("text");
+
+ b.Property("GuidePersonaPrompt")
+ .HasColumnType("text");
+
+ b.Property("GuideVoiceId")
+ .HasColumnType("text");
+
+ b.Property("HasAdvancedStats")
+ .HasColumnType("boolean");
+
+ b.Property("HasStats")
+ .HasColumnType("boolean");
+
+ b.Property("IsActive")
+ .HasColumnType("boolean");
+
+ b.Property("IsAssistant")
+ .HasColumnType("boolean");
+
+ b.Property("IsImageWatermark")
+ .HasColumnType("boolean");
+
+ b.Property("IsMobile")
+ .HasColumnType("boolean");
+
+ b.Property("IsPushNotification")
+ .HasColumnType("boolean");
+
+ b.Property("IsTablet")
+ .HasColumnType("boolean");
+
+ b.Property("IsTrialActive")
+ .HasColumnType("boolean");
+
+ b.Property("IsVR")
+ .HasColumnType("boolean");
+
+ b.Property("IsVisitorQuestionCollectionEnabled")
+ .HasColumnType("boolean");
+
+ b.Property("IsWeb")
+ .HasColumnType("boolean");
+
+ b.Property("Name")
+ .IsRequired()
+ .HasColumnType("text");
+
+ b.Property("PinCode")
+ .HasColumnType("text");
+
+ b.Property("PublicApiKey")
+ .HasColumnType("text");
+
+ b.Property("StatsHistoryDays")
+ .HasColumnType("integer");
+
+ b.Property("StorageQuotaBytes")
+ .HasColumnType("bigint");
+
+ b.Property("StripeCustomerId")
+ .HasColumnType("text");
+
+ b.Property("StripeSubscriptionId")
+ .HasColumnType("text");
+
+ b.Property("SubscriptionPlanId")
+ .HasColumnType("text");
+
+ b.Property("TrialAiTokensUsed")
+ .HasColumnType("bigint");
+
+ b.Property("TrialCheckInEmailSent")
+ .HasColumnType("boolean");
+
+ b.Property("TrialEndsAt")
+ .HasColumnType("timestamp with time zone");
+
+ b.Property("TrialLastDayEmailSent")
+ .HasColumnType("boolean");
+
+ b.Property("TrialReminderEmailSent")
+ .HasColumnType("boolean");
+
+ b.Property("VatNumber")
+ .HasColumnType("text");
+
+ b.Property("VatRate")
+ .HasColumnType("numeric");
+
+ b.Property("WebSlug")
+ .HasColumnType("text");
+
+ b.HasKey("Id");
+
+ b.HasIndex("SubscriptionPlanId");
+
+ b.ToTable("Instances");
+ });
+
+ modelBuilder.Entity("ManagerService.Data.PushNotification", b =>
+ {
+ b.Property("Id")
+ .HasColumnType("text");
+
+ b.Property("Body")
+ .IsRequired()
+ .HasColumnType("text");
+
+ b.Property("DateCreation")
+ .HasColumnType("timestamp with time zone");
+
+ b.Property("HangfireJobId")
+ .HasColumnType("text");
+
+ b.Property("InstanceId")
+ .IsRequired()
+ .HasColumnType("text");
+
+ b.Property("ScheduledAt")
+ .HasColumnType("timestamp with time zone");
+
+ b.Property("SentAt")
+ .HasColumnType("timestamp with time zone");
+
+ b.Property("Status")
+ .HasColumnType("integer");
+
+ b.Property("Title")
+ .IsRequired()
+ .HasColumnType("text");
+
+ b.Property("Topic")
+ .IsRequired()
+ .HasColumnType("text");
+
+ b.HasKey("Id");
+
+ b.HasIndex("InstanceId");
+
+ b.ToTable("PushNotifications");
+ });
+
+ modelBuilder.Entity("ManagerService.Data.QuestionThemeMonthly", b =>
+ {
+ b.Property("Id")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("bigint");
+
+ NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id"));
+
+ b.Property("Count")
+ .HasColumnType("integer");
+
+ b.Property("InstanceId")
+ .HasColumnType("text");
+
+ b.Property("Month")
+ .HasColumnType("timestamp with time zone");
+
+ b.Property("Theme")
+ .HasColumnType("text");
+
+ b.HasKey("Id");
+
+ b.HasIndex("InstanceId", "Month");
+
+ b.ToTable("QuestionThemeMonthlies");
+ });
+
+ modelBuilder.Entity("ManagerService.Data.Resource", b =>
+ {
+ b.Property("Id")
+ .HasColumnType("text");
+
+ b.Property("AiChunkCount")
+ .HasColumnType("integer");
+
+ b.Property("AiIndexMessage")
+ .HasColumnType("text");
+
+ b.Property("AiIndexStatus")
+ .HasColumnType("integer");
+
+ b.Property("AiIndexedAt")
+ .HasColumnType("timestamp with time zone");
+
+ b.Property("DateCreation")
+ .HasColumnType("timestamp with time zone");
+
+ b.Property("DateUpdate")
+ .HasColumnType("timestamp with time zone");
+
+ b.Property("FileName")
+ .HasColumnType("text");
+
+ b.Property("Height")
+ .HasColumnType("integer");
+
+ b.Property("IncludeInAiKnowledge")
+ .HasColumnType("boolean");
+
+ b.Property("InstanceId")
+ .IsRequired()
+ .HasColumnType("text");
+
+ b.Property("Label")
+ .IsRequired()
+ .HasColumnType("text");
+
+ b.Property("SizeBytes")
+ .HasColumnType("bigint");
+
+ b.Property("StoragePath")
+ .HasColumnType("text");
+
+ b.Property("Type")
+ .HasColumnType("integer");
+
+ b.Property("Url")
+ .HasColumnType("text");
+
+ b.Property("Width")
+ .HasColumnType("integer");
+
+ b.HasKey("Id");
+
+ b.HasIndex("InstanceId");
+
+ b.ToTable("Resources");
+ });
+
+ modelBuilder.Entity("ManagerService.Data.Section", b =>
+ {
+ b.Property("Id")
+ .HasColumnType("text");
+
+ b.Property("BeaconId")
+ .HasColumnType("integer");
+
+ b.Property("ConfigurationId")
+ .IsRequired()
+ .HasColumnType("text");
+
+ b.Property("DateCreation")
+ .HasColumnType("timestamp with time zone");
+
+ b.Property("DateUpdate")
+ .HasColumnType("timestamp with time zone");
+
+ b.Property("Description")
+ .HasColumnType("jsonb");
+
+ b.Property("Discriminator")
+ .IsRequired()
+ .HasMaxLength(50)
+ .HasColumnType("character varying(50)");
+
+ b.Property("ImageId")
+ .HasColumnType("text");
+
+ b.Property("ImageSource")
+ .HasColumnType("text");
+
+ b.Property("InstanceId")
+ .IsRequired()
+ .HasColumnType("text");
+
+ b.Property("IsActive")
+ .HasColumnType("boolean");
+
+ b.Property("IsBeacon")
+ .HasColumnType("boolean");
+
+ b.Property("IsSubSection")
+ .HasColumnType("boolean");
+
+ b.Property("Label")
+ .IsRequired()
+ .HasColumnType("text");
+
+ b.Property("Latitude")
+ .HasColumnType("text");
+
+ b.Property("Longitude")
+ .HasColumnType("text");
+
+ b.Property("MeterZoneGPS")
+ .HasColumnType("integer");
+
+ b.Property("Order")
+ .HasColumnType("integer");
+
+ b.Property("ParentId")
+ .HasColumnType("text");
+
+ b.Property("SectionMenuId")
+ .HasColumnType("text");
+
+ b.Property("Title")
+ .IsRequired()
+ .HasColumnType("jsonb");
+
+ b.Property("Type")
+ .HasColumnType("integer");
+
+ b.HasKey("Id");
+
+ b.HasIndex("InstanceId");
+
+ b.HasIndex("SectionMenuId");
+
+ b.ToTable("Sections");
+
+ b.HasDiscriminator().HasValue("Base");
+
+ b.UseTphMappingStrategy();
+ });
+
+ modelBuilder.Entity("ManagerService.Data.SubSection.EventAgenda", b =>
+ {
+ b.Property("Id")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("integer");
+
+ NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id"));
+
+ b.Property("Address")
+ .HasColumnType("jsonb");
+
+ b.Property("DateAdded")
+ .HasColumnType("timestamp with time zone");
+
+ b.Property("DateFrom")
+ .HasColumnType("timestamp with time zone");
+
+ b.Property("DateTo")
+ .HasColumnType("timestamp with time zone");
+
+ b.Property("Description")
+ .IsRequired()
+ .HasColumnType("jsonb");
+
+ b.Property("Email")
+ .HasColumnType("text");
+
+ b.Property("IdVideoYoutube")
+ .HasColumnType("text");
+
+ b.Property("IsSynced")
+ .HasColumnType("boolean");
+
+ b.Property("Label")
+ .IsRequired()
+ .HasColumnType("jsonb");
+
+ b.Property("Phone")
+ .HasColumnType("text");
+
+ b.Property("ResourceId")
+ .HasColumnType("text");
+
+ b.Property("SectionAgendaId")
+ .HasColumnType("text");
+
+ b.Property("SectionEventId")
+ .HasColumnType("text");
+
+ b.Property("SyncedImageUrl")
+ .HasColumnType("text");
+
+ b.Property("Type")
+ .HasColumnType("text");
+
+ b.Property("VideoLink")
+ .HasColumnType("text");
+
+ b.Property("VideoResourceId")
+ .HasColumnType("text");
+
+ b.Property("Website")
+ .HasColumnType("text");
+
+ b.HasKey("Id");
+
+ b.HasIndex("ResourceId");
+
+ b.HasIndex("SectionAgendaId");
+
+ b.HasIndex("SectionEventId");
+
+ b.HasIndex("VideoResourceId");
+
+ b.ToTable("EventAgendas");
+ });
+
+ modelBuilder.Entity("ManagerService.Data.SubSection.GeoPoint", b =>
+ {
+ b.Property("Id")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("integer");
+
+ NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id"));
+
+ b.Property("CategorieId")
+ .HasColumnType("integer");
+
+ b.Property("Contents")
+ .IsRequired()
+ .HasColumnType("jsonb");
+
+ b.Property("Description")
+ .IsRequired()
+ .HasColumnType("jsonb");
+
+ b.Property("Email")
+ .IsRequired()
+ .HasColumnType("jsonb");
+
+ b.Property("Geometry")
+ .HasColumnType("geometry");
+
+ b.Property("ImageResourceId")
+ .HasColumnType("text");
+
+ b.Property("ImageUrl")
+ .HasColumnType("text");
+
+ b.Property("Phone")
+ .IsRequired()
+ .HasColumnType("jsonb");
+
+ b.Property("PolyColor")
+ .HasColumnType("text");
+
+ b.Property("Prices")
+ .IsRequired()
+ .HasColumnType("jsonb");
+
+ b.Property("Schedules")
+ .IsRequired()
+ .HasColumnType("jsonb");
+
+ b.Property("SectionEventId")
+ .HasColumnType("text");
+
+ b.Property("SectionMapId")
+ .HasColumnType("text");
+
+ b.Property("Site")
+ .IsRequired()
+ .HasColumnType("jsonb");
+
+ b.Property("Title")
+ .IsRequired()
+ .HasColumnType("jsonb");
+
+ b.HasKey("Id");
+
+ b.HasIndex("SectionEventId");
+
+ b.HasIndex("SectionMapId");
+
+ b.ToTable("GeoPoints");
+ });
+
+ modelBuilder.Entity("ManagerService.Data.SubSection.GuidedPath", b =>
+ {
+ b.Property("Id")
+ .HasColumnType("text");
+
+ b.Property("DateCreation")
+ .HasColumnType("timestamp with time zone");
+
+ b.Property("DateUpdate")
+ .HasColumnType("timestamp with time zone");
+
+ b.Property("Description")
+ .HasColumnType("jsonb");
+
+ b.Property("EstimatedDurationMinutes")
+ .HasColumnType("integer");
+
+ b.Property("GameMessageDebut")
+ .HasColumnType("jsonb");
+
+ b.Property("GameMessageFin")
+ .HasColumnType("jsonb");
+
+ b.Property("HideNextStepsUntilComplete")
+ .HasColumnType("boolean");
+
+ b.Property("ImageResourceId")
+ .HasColumnType("text");
+
+ b.Property("InstanceId")
+ .IsRequired()
+ .HasColumnType("text");
+
+ b.Property("IsGameMode")
+ .HasColumnType("boolean");
+
+ b.Property("IsLinear")
+ .HasColumnType("boolean");
+
+ b.Property("Order")
+ .HasColumnType("integer");
+
+ b.Property("RequireSuccessToAdvance")
+ .HasColumnType("boolean");
+
+ b.Property("SectionEventId")
+ .HasColumnType("text");
+
+ b.Property("SectionParcoursId")
+ .HasColumnType("text");
+
+ b.Property