diff --git a/ManagerService/Controllers/AiController.cs b/ManagerService/Controllers/AiController.cs
index 5401475..0b5faa1 100644
--- a/ManagerService/Controllers/AiController.cs
+++ b/ManagerService/Controllers/AiController.cs
@@ -31,6 +31,12 @@ namespace ManagerService.Controllers
_logger = logger;
}
+ private string? GetCallerInstanceId() =>
+ User.FindFirst(ManagerService.Service.Security.ClaimTypes.InstanceId)?.Value;
+
+ private bool IsSuperAdmin() =>
+ User.HasClaim(ManagerService.Service.Security.ClaimTypes.Permission, ManagerService.Service.Security.Permissions.SuperAdmin);
+
///
/// Traduit un texte HTML vers plusieurs langues via IA
///
@@ -42,6 +48,9 @@ namespace ManagerService.Controllers
{
try
{
+ if (!IsSuperAdmin() && instanceId != GetCallerInstanceId())
+ return Forbid();
+
var instance = _context.Instances.FirstOrDefault(i => i.Id == instanceId);
if (instance == null || !instance.IsAssistant)
return Forbid();
@@ -81,6 +90,9 @@ namespace ManagerService.Controllers
{
try
{
+ if (!IsSuperAdmin() && request.InstanceId != GetCallerInstanceId())
+ return Forbid();
+
// Vérifie que l'instance a activé la fonctionnalité assistant
var instance = _context.Instances
.FirstOrDefault(i => i.Id == request.InstanceId);
diff --git a/ManagerService/Controllers/DeviceController.cs b/ManagerService/Controllers/DeviceController.cs
index 1411dfc..96e5f30 100644
--- a/ManagerService/Controllers/DeviceController.cs
+++ b/ManagerService/Controllers/DeviceController.cs
@@ -34,6 +34,12 @@ namespace ManagerService.Controllers
_myInfoMateDbContext = myInfoMateDbContext;
}
+ private string? GetCallerInstanceId() =>
+ User.FindFirst(ManagerService.Service.Security.ClaimTypes.InstanceId)?.Value;
+
+ private bool IsSuperAdmin() =>
+ User.HasClaim(ManagerService.Service.Security.ClaimTypes.Permission, ManagerService.Service.Security.Permissions.SuperAdmin);
+
///
/// Get a list of all devices
///
@@ -45,10 +51,13 @@ namespace ManagerService.Controllers
{
try
{
- //List devices = _deviceService.GetAll(instanceId);
- List devices = _myInfoMateDbContext.Devices.Include(d => d.Configuration).ToList();
+ var scopedInstanceId = IsSuperAdmin() ? instanceId : GetCallerInstanceId();
- return new OkObjectResult(devices.Select(d => d.ToDTO()));
+ var query = _myInfoMateDbContext.Devices.Include(d => d.Configuration).AsQueryable();
+ if (scopedInstanceId != null)
+ query = query.Where(d => d.InstanceId == scopedInstanceId);
+
+ return new OkObjectResult(query.ToList().Select(d => d.ToDTO()));
}
catch (Exception ex)
{
@@ -60,8 +69,7 @@ namespace ManagerService.Controllers
///
/// Get a specific device
///
- /// id device
- [AllowAnonymous]
+ /// id device
[ProducesResponseType(typeof(DeviceDetailDTO), 200)]
[ProducesResponseType(typeof(string), 404)]
[ProducesResponseType(typeof(string), 500)]
@@ -73,7 +81,7 @@ namespace ManagerService.Controllers
//OldDevice device = _deviceService.GetById(id);
Device device = _myInfoMateDbContext.Devices.Include(d => d.Configuration).FirstOrDefault(i => i.Id == id);
- if (device == null)
+ if (device == null || (!IsSuperAdmin() && device.InstanceId != GetCallerInstanceId()))
throw new KeyNotFoundException("This device was not found");
return new OkObjectResult(device.ToDetailDTO());
@@ -91,8 +99,7 @@ namespace ManagerService.Controllers
///
/// Create a new device
///
- /// New device info
- [AllowAnonymous]
+ /// New device info
[ProducesResponseType(typeof(DeviceDetailDTO), 200)]
[ProducesResponseType(typeof(string), 400)]
[ProducesResponseType(typeof(string), 404)]
@@ -106,6 +113,9 @@ namespace ManagerService.Controllers
if (newDevice == null)
throw new ArgumentNullException("Device param is null");
+ if (!IsSuperAdmin() && newDevice.instanceId != GetCallerInstanceId())
+ throw new UnauthorizedAccessException("Cannot create a device for another instance");
+
//var configuration = _configurationService.GetById(newDevice.configurationId);
var configuration = _myInfoMateDbContext.Configurations.FirstOrDefault(c => c.Id == newDevice.configurationId);
@@ -144,6 +154,9 @@ namespace ManagerService.Controllers
ApplicationInstance applicationInstance = _myInfoMateDbContext.ApplicationInstances.FirstOrDefault(ai => ai.InstanceId == newDevice.instanceId && ai.AppType == AppType.Tablet);
+ if (applicationInstance == null)
+ throw new KeyNotFoundException("Application instance does not exist");
+
//OldDevice deviceCreated = _deviceService.IsExistIdentifier(newDevice.identifier) ? _deviceService.Update(device.Id, device) : _deviceService.Create(device);
if (deviceDB != null)
{
@@ -172,6 +185,10 @@ namespace ManagerService.Controllers
{
return new BadRequestObjectResult(ex.Message) { };
}
+ catch (UnauthorizedAccessException ex)
+ {
+ return new ObjectResult(ex.Message) { StatusCode = 403 };
+ }
catch (KeyNotFoundException ex)
{
return new NotFoundObjectResult(ex.Message) { };
@@ -206,10 +223,12 @@ namespace ManagerService.Controllers
//OldDevice device = _deviceService.GetById(updatedDevice.id);
Device device = _myInfoMateDbContext.Devices.FirstOrDefault(d => d.Id == updatedDevice.id);
- if (device == null)
+ if (device == null || (!IsSuperAdmin() && device.InstanceId != GetCallerInstanceId()))
throw new KeyNotFoundException("Device does not exist");
- // Todo add some verification ?
+ if (!IsSuperAdmin() && updatedDevice.instanceId != device.InstanceId)
+ throw new UnauthorizedAccessException("Cannot move a device to another instance");
+
device.Name = updatedDevice.name;
device.InstanceId = updatedDevice.instanceId;
device.Identifier = updatedDevice.identifier;
@@ -230,6 +249,10 @@ namespace ManagerService.Controllers
{
return new BadRequestObjectResult(ex.Message) { };
}
+ catch (UnauthorizedAccessException ex)
+ {
+ return new ObjectResult(ex.Message) { StatusCode = 403 };
+ }
catch (KeyNotFoundException ex)
{
return new NotFoundObjectResult(ex.Message) { };
@@ -259,7 +282,7 @@ namespace ManagerService.Controllers
//OldDevice device = _deviceService.GetById(deviceIn.id);
Device device = _myInfoMateDbContext.Devices.FirstOrDefault(d => d.Id == deviceIn.id);
- if (device == null)
+ if (device == null || (!IsSuperAdmin() && device.InstanceId != GetCallerInstanceId()))
throw new KeyNotFoundException("Device does not exist");
//var configuration = _configurationService.GetById(deviceIn.configurationId);
@@ -314,7 +337,7 @@ namespace ManagerService.Controllers
Device device = _myInfoMateDbContext.Devices.FirstOrDefault(d => d.Id == id);
- if (device == null)
+ if (device == null || (!IsSuperAdmin() && device.InstanceId != GetCallerInstanceId()))
throw new KeyNotFoundException("Device does not exist");
_myInfoMateDbContext.Remove(device);
diff --git a/ManagerService/Controllers/StatsController.cs b/ManagerService/Controllers/StatsController.cs
index 80c938a..2a200a1 100644
--- a/ManagerService/Controllers/StatsController.cs
+++ b/ManagerService/Controllers/StatsController.cs
@@ -25,6 +25,12 @@ namespace ManagerService.Controllers
_db = db;
}
+ private string? GetCallerInstanceId() =>
+ User.FindFirst(ManagerService.Service.Security.ClaimTypes.InstanceId)?.Value;
+
+ private bool IsSuperAdmin() =>
+ User.HasClaim(ManagerService.Service.Security.ClaimTypes.Permission, ManagerService.Service.Security.Permissions.SuperAdmin);
+
/// Track a single visit event (anonymous)
[AllowAnonymous]
[HttpPost("event")]
@@ -81,6 +87,9 @@ namespace ManagerService.Controllers
if (string.IsNullOrEmpty(instanceId))
return BadRequest("instanceId is required");
+ if (!IsSuperAdmin() && instanceId != GetCallerInstanceId())
+ return Forbid();
+
var instance = _db.Instances
.FirstOrDefault(i => i.Id == instanceId);
diff --git a/ManagerService/Controllers/UserController.cs b/ManagerService/Controllers/UserController.cs
index 488ad3f..b8ceff2 100644
--- a/ManagerService/Controllers/UserController.cs
+++ b/ManagerService/Controllers/UserController.cs
@@ -85,7 +85,7 @@ namespace ManagerService.Controllers
{
User user = _myInfoMateDbContext.Users.FirstOrDefault(i => i.Id == id);
- if (user == null)
+ if (user == null || (!IsSuperAdmin() && user.InstanceId != GetCallerInstanceId()))
throw new KeyNotFoundException("This user was not found");
return new OkObjectResult(user.ToDTO());
@@ -127,7 +127,7 @@ namespace ManagerService.Controllers
throw new UnauthorizedAccessException("Cannot assign a role higher than your own");
User newUser = new User();
- newUser.InstanceId = newUserDTO.instanceId;
+ newUser.InstanceId = IsSuperAdmin() ? newUserDTO.instanceId : GetCallerInstanceId();
newUser.Email = newUserDTO.email;
newUser.FirstName = newUserDTO.firstName;
newUser.LastName = newUserDTO.lastName;
@@ -182,9 +182,12 @@ namespace ManagerService.Controllers
User user = _myInfoMateDbContext.Users.FirstOrDefault(u => u.Id == updatedUser.id);
- if (user == null)
+ if (user == null || (!IsSuperAdmin() && user.InstanceId != GetCallerInstanceId()))
throw new KeyNotFoundException("User does not exist");
+ if (!IsSuperAdmin() && user.Role < GetCallerRole())
+ throw new UnauthorizedAccessException("Cannot modify a user with a higher role than your own");
+
user.FirstName = updatedUser.firstName;
user.LastName = updatedUser.lastName;
@@ -235,9 +238,12 @@ namespace ManagerService.Controllers
User user = _myInfoMateDbContext.Users.FirstOrDefault(u => u.Id == id);
- if (user == null)
+ if (user == null || (!IsSuperAdmin() && user.InstanceId != GetCallerInstanceId()))
throw new KeyNotFoundException("User does not exist");
+ if (!IsSuperAdmin() && user.Role < GetCallerRole())
+ throw new UnauthorizedAccessException("Cannot delete a user with a higher role than your own");
+
_myInfoMateDbContext.Remove(user);
_myInfoMateDbContext.SaveChanges();
@@ -247,6 +253,10 @@ namespace ManagerService.Controllers
{
return new BadRequestObjectResult(ex.Message) { };
}
+ catch (UnauthorizedAccessException ex)
+ {
+ return new ObjectResult(ex.Message) { StatusCode = 403 };
+ }
catch (KeyNotFoundException ex)
{
return new NotFoundObjectResult(ex.Message) { };
diff --git a/ManagerService/DTOs/AppConfigurationLinkDTO.cs b/ManagerService/DTOs/AppConfigurationLinkDTO.cs
index 7f05644..5a78ded 100644
--- a/ManagerService/DTOs/AppConfigurationLinkDTO.cs
+++ b/ManagerService/DTOs/AppConfigurationLinkDTO.cs
@@ -16,7 +16,9 @@ namespace ManagerService.DTOs
public bool isActive { get; set; } = true;
- public int? weightMasonryGrid { get; set; } // Specific Mobile
+ public int? gridColSpan { get; set; } // Specific Mobile & Web — nb de colonnes occupées (bento)
+
+ public int? gridRowSpan { get; set; } // Specific Mobile & Web — nb de lignes occupées (bento)
public bool isDate { get; set; } // Specific Kiosk
@@ -32,8 +34,6 @@ namespace ManagerService.DTOs
public DeviceDTO device { get; set; } // Specific Kiosk
- public LayoutMainPageType layoutMainPage { get; set; } // Specific Kiosk
-
public string loaderImageId { get; set; } // Specific Kiosk
public string loaderImageUrl { get; set; } // Specific Kiosk
diff --git a/ManagerService/DTOs/ApplicationInstanceDTO.cs b/ManagerService/DTOs/ApplicationInstanceDTO.cs
index 23de456..48a9210 100644
--- a/ManagerService/DTOs/ApplicationInstanceDTO.cs
+++ b/ManagerService/DTOs/ApplicationInstanceDTO.cs
@@ -26,8 +26,6 @@ namespace ManagerService.DTOs
public string secondaryColor { get; set; }
- public LayoutMainPageType layoutMainPage { get; set; }
-
public List languages { get; set; }
public string sectionEventId { get; set; }
diff --git a/ManagerService/Data/AppConfigurationLink.cs b/ManagerService/Data/AppConfigurationLink.cs
index 8e6cc81..2c907cf 100644
--- a/ManagerService/Data/AppConfigurationLink.cs
+++ b/ManagerService/Data/AppConfigurationLink.cs
@@ -28,7 +28,9 @@ namespace ManagerService.Data
public bool IsActive { get; set; } = true; // Specific Mobile
- public int? WeightMasonryGrid { get; set; } // Specific Mobile
+ public int? GridColSpan { get; set; } // Specific Mobile & Web — nb de colonnes occupées (bento)
+
+ public int? GridRowSpan { get; set; } // Specific Mobile & Web — nb de lignes occupées (bento)
public bool IsDate { get; set; } // Specific Kiosk
@@ -40,8 +42,6 @@ namespace ManagerService.Data
public bool IsSectionImageBackground { get; set; } // => Chose layout of main page // Specific Kiosk
- public LayoutMainPageType LayoutMainPage { get; set; } = LayoutMainPageType.MasonryGrid; // Specific Kiosk and mobile
-
public string LoaderImageId { get; set; } // Specific Kiosk
public string LoaderImageUrl { get; set; } // Specific Kiosk
@@ -70,7 +70,8 @@ namespace ManagerService.Data
applicationInstanceId = ApplicationInstanceId,
order = Order,
isActive = IsActive,
- weightMasonryGrid = WeightMasonryGrid,
+ gridColSpan = GridColSpan,
+ gridRowSpan = GridRowSpan,
primaryColor = PrimaryColor,
secondaryColor = SecondaryColor,
loaderImageId = LoaderImageId,
@@ -91,7 +92,8 @@ namespace ManagerService.Data
ApplicationInstanceId = appConfigurationLinkDTO.applicationInstanceId;
Order = appConfigurationLinkDTO.order;
IsActive = appConfigurationLinkDTO.isActive;
- WeightMasonryGrid = appConfigurationLinkDTO?.weightMasonryGrid;
+ GridColSpan = appConfigurationLinkDTO?.gridColSpan;
+ GridRowSpan = appConfigurationLinkDTO?.gridRowSpan;
PrimaryColor = appConfigurationLinkDTO.primaryColor;
SecondaryColor = appConfigurationLinkDTO.secondaryColor;
LoaderImageId = appConfigurationLinkDTO.loaderImageId;
diff --git a/ManagerService/Data/ApplicationInstance.cs b/ManagerService/Data/ApplicationInstance.cs
index 2e98260..3b20b10 100644
--- a/ManagerService/Data/ApplicationInstance.cs
+++ b/ManagerService/Data/ApplicationInstance.cs
@@ -36,9 +36,7 @@ namespace ManagerService.Data
public string PrimaryColor { get; set; } // Specific Mobile et web
- public string SecondaryColor { get; set; } // Specific Mobile et web
-
- public LayoutMainPageType LayoutMainPage { get; set; } = LayoutMainPageType.MasonryGrid; // Specific Mobile et web
+ public string SecondaryColor { get; set; } // Specific Mobile et web
public List Languages { get; set; } // All app must support languages, if not, client's problem
@@ -71,7 +69,6 @@ namespace ManagerService.Data
loaderImageUrl = LoaderImageUrl,
primaryColor = PrimaryColor,
secondaryColor = SecondaryColor,
- layoutMainPage = LayoutMainPage,
languages = Languages,
sectionEventId = SectionEventId,
sectionEventDTO = sectionEventDTO,
@@ -89,7 +86,6 @@ namespace ManagerService.Data
LoaderImageUrl = dto.loaderImageUrl;
PrimaryColor = dto.primaryColor;
SecondaryColor = dto.secondaryColor;
- LayoutMainPage = dto.layoutMainPage;
Languages = dto.languages;
Configurations = dto.configurations;
SectionEventId = dto.sectionEventId;
@@ -108,10 +104,4 @@ namespace ManagerService.Data
VR,
Voice // Lunettes Ray-Ban Meta / interface vocale — pas de navigation UI
}
-
- public enum LayoutMainPageType
- {
- SimpleGrid,
- MasonryGrid
- }
}
diff --git a/ManagerService/Migrations/20260710144315_RemoveLayoutMainPageType_WeightToDouble.Designer.cs b/ManagerService/Migrations/20260710144315_RemoveLayoutMainPageType_WeightToDouble.Designer.cs
new file mode 100644
index 0000000..392df12
--- /dev/null
+++ b/ManagerService/Migrations/20260710144315_RemoveLayoutMainPageType_WeightToDouble.Designer.cs
@@ -0,0 +1,1628 @@
+//
+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;
+
+#nullable disable
+
+namespace ManagerService.Migrations
+{
+ [DbContext(typeof(MyInfoMateDbContext))]
+ [Migration("20260710144315_RemoveLayoutMainPageType_WeightToDouble")]
+ partial class RemoveLayoutMainPageType_WeightToDouble
+ {
+ ///
+ 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.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("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("DeviceId")
+ .HasColumnType("text");
+
+ 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.Property("WeightMasonryGrid")
+ .HasColumnType("double precision");
+
+ 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("AppType")
+ .HasColumnType("integer");
+
+ b.Property("InstanceId")
+ .IsRequired()
+ .HasColumnType("text");
+
+ b.Property("IsAssistant")
+ .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("PrimaryColor")
+ .HasColumnType("text");
+
+ b.Property("SecondaryColor")
+ .HasColumnType("text");
+
+ b.Property("SectionEventId")
+ .HasColumnType("text");
+
+ b.HasKey("Id");
+
+ b.HasIndex("SectionEventId");
+
+ 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.ToTable("AuditLogs");
+ });
+
+ modelBuilder.Entity("ManagerService.Data.Configuration", b =>
+ {
+ b.Property("Id")
+ .HasColumnType("text");
+
+ b.Property("DateCreation")
+ .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.ToTable("Configurations");
+ });
+
+ 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.ToTable("Devices");
+ });
+
+ modelBuilder.Entity("ManagerService.Data.Instance", b =>
+ {
+ b.Property("Id")
+ .HasColumnType("text");
+
+ b.Property("AiRequestsPerMonth")
+ .HasColumnType("integer");
+
+ b.Property("AiRequestsThisMonth")
+ .HasColumnType("integer");
+
+ b.Property("AiUsageMonthKey")
+ .HasColumnType("text");
+
+ b.Property("DateCreation")
+ .HasColumnType("timestamp with time zone");
+
+ b.Property("HasAdvancedStats")
+ .HasColumnType("boolean");
+
+ b.Property("HasStats")
+ .HasColumnType("boolean");
+
+ b.Property("IsAssistant")
+ .HasColumnType("boolean");
+
+ b.Property("IsMobile")
+ .HasColumnType("boolean");
+
+ b.Property("IsPushNotification")
+ .HasColumnType("boolean");
+
+ b.Property("IsTablet")
+ .HasColumnType("boolean");
+
+ b.Property("IsVR")
+ .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("SubscriptionPlanId")
+ .HasColumnType("text");
+
+ 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.Resource", b =>
+ {
+ b.Property("Id")
+ .HasColumnType("text");
+
+ b.Property("DateCreation")
+ .HasColumnType("timestamp with time zone");
+
+ b.Property("InstanceId")
+ .IsRequired()
+ .HasColumnType("text");
+
+ b.Property("Label")
+ .IsRequired()
+ .HasColumnType("text");
+
+ b.Property("SizeBytes")
+ .HasColumnType("bigint");
+
+ b.Property("Type")
+ .HasColumnType("integer");
+
+ b.Property("Url")
+ .HasColumnType("text");
+
+ b.HasKey("Id");
+
+ 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("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("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("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("Title")
+ .IsRequired()
+ .HasColumnType("jsonb");
+
+ b.HasKey("Id");
+
+ b.HasIndex("ImageResourceId");
+
+ b.HasIndex("SectionEventId");
+
+ b.HasIndex("SectionParcoursId");
+
+ b.ToTable("GuidedPaths");
+ });
+
+ modelBuilder.Entity("ManagerService.Data.SubSection.GuidedStep", b =>
+ {
+ b.Property("Id")
+ .HasColumnType("text");
+
+ b.Property("AudioIds")
+ .HasColumnType("jsonb");
+
+ b.Property("Contents")
+ .HasColumnType("jsonb");
+
+ b.Property("Description")
+ .HasColumnType("jsonb");
+
+ b.Property("FactContent")
+ .HasColumnType("jsonb");
+
+ b.Property("Geometry")
+ .HasColumnType("geometry");
+
+ b.Property("GuidedPathId")
+ .IsRequired()
+ .HasColumnType("text");
+
+ b.Property("ImageUrl")
+ .HasColumnType("text");
+
+ b.Property("IsGeoTriggered")
+ .HasColumnType("boolean");
+
+ b.Property("IsHiddenInitially")
+ .HasColumnType("boolean");
+
+ b.Property("IsStepLocked")
+ .HasColumnType("boolean");
+
+ b.Property("IsStepTimer")
+ .HasColumnType("boolean");
+
+ b.Property("Order")
+ .HasColumnType("integer");
+
+ b.Property("TimerExpiredMessage")
+ .HasColumnType("jsonb");
+
+ b.Property("TimerSeconds")
+ .HasColumnType("integer");
+
+ b.Property("Title")
+ .IsRequired()
+ .HasColumnType("jsonb");
+
+ b.Property("ZoneRadiusMeters")
+ .HasColumnType("double precision");
+
+ b.HasKey("Id");
+
+ b.HasIndex("GuidedPathId");
+
+ b.ToTable("GuidedSteps");
+ });
+
+ modelBuilder.Entity("ManagerService.Data.SubSection.QuizQuestion", b =>
+ {
+ b.Property("Id")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("integer");
+
+ NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id"));
+
+ b.Property("GuidedStepId")
+ .HasColumnType("text");
+
+ b.Property("IsSlidingPuzzle")
+ .HasColumnType("boolean");
+
+ b.Property>("Label")
+ .IsRequired()
+ .HasColumnType("jsonb");
+
+ b.Property("Order")
+ .HasColumnType("integer");
+
+ b.Property("PuzzleCols")
+ .HasColumnType("integer");
+
+ b.Property("PuzzleImageId")
+ .HasColumnType("text");
+
+ b.Property("PuzzleRows")
+ .HasColumnType("integer");
+
+ b.Property("ResourceId")
+ .HasColumnType("text");
+
+ b.Property>("Responses")
+ .IsRequired()
+ .HasColumnType("jsonb");
+
+ b.Property("SectionQuizId")
+ .HasColumnType("text");
+
+ b.Property("ValidationQuestionType")
+ .HasColumnType("integer");
+
+ b.HasKey("Id");
+
+ b.HasIndex("GuidedStepId");
+
+ b.HasIndex("PuzzleImageId");
+
+ b.HasIndex("ResourceId");
+
+ b.HasIndex("SectionQuizId");
+
+ b.ToTable("QuizQuestions");
+ });
+
+ modelBuilder.Entity("ManagerService.Data.SubSection.SectionEvent+MapAnnotation", b =>
+ {
+ b.Property("Id")
+ .HasColumnType("text");
+
+ b.Property("Geometry")
+ .HasColumnType("geometry");
+
+ b.Property("GeometryType")
+ .HasColumnType("integer");
+
+ b.Property("Icon")
+ .HasColumnType("text");
+
+ b.Property("IconResourceId")
+ .HasColumnType("text");
+
+ b.Property>("Label")
+ .HasColumnType("jsonb");
+
+ b.Property("PolyColor")
+ .HasColumnType("text");
+
+ b.Property("ProgrammeBlockId")
+ .HasColumnType("text");
+
+ b.Property("SectionEventId")
+ .HasColumnType("text");
+
+ b.Property>("Type")
+ .HasColumnType("jsonb");
+
+ b.HasKey("Id");
+
+ b.HasIndex("IconResourceId");
+
+ b.HasIndex("ProgrammeBlockId");
+
+ b.HasIndex("SectionEventId");
+
+ b.ToTable("MapAnnotations");
+ });
+
+ modelBuilder.Entity("ManagerService.Data.SubSection.SectionEvent+ProgrammeBlock", b =>
+ {
+ b.Property("Id")
+ .HasColumnType("text");
+
+ b.Property>("Description")
+ .HasColumnType("jsonb");
+
+ b.Property("EndTime")
+ .HasColumnType("timestamp with time zone");
+
+ b.Property("SectionEventId")
+ .HasColumnType("text");
+
+ b.Property("StartTime")
+ .HasColumnType("timestamp with time zone");
+
+ b.Property>("Title")
+ .HasColumnType("jsonb");
+
+ b.HasKey("Id");
+
+ b.HasIndex("SectionEventId");
+
+ b.ToTable("ProgrammeBlocks");
+ });
+
+ modelBuilder.Entity("ManagerService.Data.SubscriptionPlan", b =>
+ {
+ b.Property("Id")
+ .HasColumnType("text");
+
+ b.Property("AiRequestsPerMonth")
+ .HasColumnType("integer");
+
+ b.Property("HasAdvancedStats")
+ .HasColumnType("boolean");
+
+ b.Property("HasStats")
+ .HasColumnType("boolean");
+
+ b.Property("Name")
+ .IsRequired()
+ .HasColumnType("text");
+
+ b.Property("StatsHistoryDays")
+ .HasColumnType("integer");
+
+ b.Property("StorageQuotaBytes")
+ .HasColumnType("bigint");
+
+ b.HasKey("Id");
+
+ b.ToTable("SubscriptionPlans");
+
+ b.HasData(
+ new
+ {
+ Id = "plan-starter",
+ AiRequestsPerMonth = 0,
+ HasAdvancedStats = false,
+ HasStats = false,
+ Name = "Starter",
+ StatsHistoryDays = 30,
+ StorageQuotaBytes = 1073741824L
+ },
+ new
+ {
+ Id = "plan-standard",
+ AiRequestsPerMonth = 500,
+ HasAdvancedStats = false,
+ HasStats = true,
+ Name = "Standard",
+ StatsHistoryDays = 30,
+ StorageQuotaBytes = 10737418240L
+ },
+ new
+ {
+ Id = "plan-premium",
+ AiRequestsPerMonth = 2000,
+ HasAdvancedStats = true,
+ HasStats = true,
+ Name = "Premium",
+ StatsHistoryDays = 0,
+ StorageQuotaBytes = 53687091200L
+ });
+ });
+
+ modelBuilder.Entity("ManagerService.Data.User", b =>
+ {
+ b.Property("Id")
+ .HasColumnType("text");
+
+ b.Property("DateCreation")
+ .HasColumnType("timestamp with time zone");
+
+ b.Property("Email")
+ .IsRequired()
+ .HasColumnType("text");
+
+ b.Property("FirstName")
+ .HasColumnType("text");
+
+ b.Property("InstanceId")
+ .IsRequired()
+ .HasColumnType("text");
+
+ b.Property("LastName")
+ .IsRequired()
+ .HasColumnType("text");
+
+ b.Property("Password")
+ .IsRequired()
+ .HasColumnType("text");
+
+ b.Property("Role")
+ .HasColumnType("integer");
+
+ b.Property("Token")
+ .IsRequired()
+ .HasColumnType("text");
+
+ b.HasKey("Id");
+
+ b.ToTable("Users");
+ });
+
+ modelBuilder.Entity("ManagerService.Data.VisitEvent", b =>
+ {
+ b.Property