Add instance index + date update audit + ai quota to token
This commit is contained in:
parent
19a0157b19
commit
f2228617ca
@ -16,7 +16,7 @@ namespace ManagerService.Tests.Controllers
|
||||
{
|
||||
public class AiControllerTests
|
||||
{
|
||||
private static readonly AiChatResponse FakeResponse = new AiChatResponse { Reply = "OK" };
|
||||
private static readonly AiChatResponse FakeResponse = new AiChatResponse { Reply = "OK", TokensUsed = 42 };
|
||||
|
||||
private AiController BuildController(MyInfoMateDbContext db, Mock<IAssistantService>? mockService = null)
|
||||
{
|
||||
@ -101,13 +101,13 @@ namespace ManagerService.Tests.Controllers
|
||||
// ── QUOTA COUNTER ────────────────────────────────────────────────────
|
||||
|
||||
[Fact]
|
||||
public async Task Chat_FirstRequestOfMonth_ResetsCounterAndSets1()
|
||||
public async Task Chat_FirstRequestOfMonth_ResetsCounterAndAddsTokensUsed()
|
||||
{
|
||||
using var db = DbContextFactory.Create();
|
||||
db.Instances.Add(new Instance
|
||||
{
|
||||
Id = "i1", Name = "Musée", IsAssistant = true, DateCreation = DateTime.UtcNow,
|
||||
AiRequestsThisMonth = 99, AiUsageMonthKey = "2020-01"
|
||||
AiTokensThisMonth = 99, AiUsageMonthKey = "2020-01"
|
||||
});
|
||||
db.ApplicationInstances.Add(new ApplicationInstance
|
||||
{
|
||||
@ -119,19 +119,19 @@ namespace ManagerService.Tests.Controllers
|
||||
await BuildController(db).Chat(MakeRequest("i1"));
|
||||
|
||||
var inst = db.Instances.First();
|
||||
Assert.Equal(1, inst.AiRequestsThisMonth);
|
||||
Assert.Equal(42, inst.AiTokensThisMonth);
|
||||
Assert.Equal(DateTime.UtcNow.ToString("yyyy-MM"), inst.AiUsageMonthKey);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task Chat_SameMonth_IncrementsCounter()
|
||||
public async Task Chat_SameMonth_AddsTokensUsedToCounter()
|
||||
{
|
||||
using var db = DbContextFactory.Create();
|
||||
var monthKey = DateTime.UtcNow.ToString("yyyy-MM");
|
||||
db.Instances.Add(new Instance
|
||||
{
|
||||
Id = "i1", Name = "Musée", IsAssistant = true, DateCreation = DateTime.UtcNow,
|
||||
AiRequestsThisMonth = 3, AiUsageMonthKey = monthKey
|
||||
AiTokensThisMonth = 3, AiUsageMonthKey = monthKey
|
||||
});
|
||||
db.ApplicationInstances.Add(new ApplicationInstance
|
||||
{
|
||||
@ -142,7 +142,34 @@ namespace ManagerService.Tests.Controllers
|
||||
|
||||
await BuildController(db).Chat(MakeRequest("i1"));
|
||||
|
||||
Assert.Equal(4, db.Instances.First().AiRequestsThisMonth);
|
||||
Assert.Equal(45, db.Instances.First().AiTokensThisMonth);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task Chat_QuotaAlreadyReached_ReturnsTooManyRequestsWithoutCallingAssistantService()
|
||||
{
|
||||
using var db = DbContextFactory.Create();
|
||||
var monthKey = DateTime.UtcNow.ToString("yyyy-MM");
|
||||
db.Instances.Add(new Instance
|
||||
{
|
||||
Id = "i1", Name = "Musée", IsAssistant = true, DateCreation = DateTime.UtcNow,
|
||||
AiTokensThisMonth = 100, AiTokensPerMonth = 100, AiUsageMonthKey = monthKey
|
||||
});
|
||||
db.ApplicationInstances.Add(new ApplicationInstance
|
||||
{
|
||||
Id = "ai1", InstanceId = "i1", AppType = AppType.Tablet, IsAssistant = true,
|
||||
Languages = new List<string>()
|
||||
});
|
||||
db.SaveChanges();
|
||||
|
||||
var mockService = new Mock<IAssistantService>();
|
||||
mockService.Setup(s => s.ChatAsync(It.IsAny<AiChatRequest>())).ReturnsAsync(FakeResponse);
|
||||
|
||||
var result = await BuildController(db, mockService).Chat(MakeRequest("i1"));
|
||||
|
||||
var status = Assert.IsType<ObjectResult>(result);
|
||||
Assert.Equal(429, status.StatusCode);
|
||||
mockService.Verify(s => s.ChatAsync(It.IsAny<AiChatRequest>()), Times.Never);
|
||||
}
|
||||
|
||||
// ── NOMINAL ──────────────────────────────────────────────────────────
|
||||
|
||||
@ -130,7 +130,7 @@ namespace ManagerService.Tests.Controllers
|
||||
var ok = Assert.IsType<OkObjectResult>(result);
|
||||
var dto = Assert.IsType<InstanceQuotaDTO>(ok.Value);
|
||||
Assert.Equal(0, dto.storageQuotaBytes);
|
||||
Assert.Equal(0, dto.aiRequestsPerMonth);
|
||||
Assert.Equal(0, dto.aiTokensPerMonth);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
@ -141,7 +141,7 @@ namespace ManagerService.Tests.Controllers
|
||||
{
|
||||
Id = "p1", Name = "Standard",
|
||||
StorageQuotaBytes = 10_000_000_000L,
|
||||
AiRequestsPerMonth = 100
|
||||
AiTokensPerMonth = 100
|
||||
});
|
||||
db.Instances.Add(new Instance
|
||||
{
|
||||
@ -154,7 +154,7 @@ namespace ManagerService.Tests.Controllers
|
||||
var ok = Assert.IsType<OkObjectResult>(result);
|
||||
var dto = Assert.IsType<InstanceQuotaDTO>(ok.Value);
|
||||
Assert.Equal(10_000_000_000L, dto.storageQuotaBytes);
|
||||
Assert.Equal(100, dto.aiRequestsPerMonth);
|
||||
Assert.Equal(100, dto.aiTokensPerMonth);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
@ -184,7 +184,7 @@ namespace ManagerService.Tests.Controllers
|
||||
db.Instances.Add(new Instance
|
||||
{
|
||||
Id = "i1", Name = "Musée", DateCreation = DateTime.UtcNow,
|
||||
AiRequestsThisMonth = 7, AiUsageMonthKey = monthKey
|
||||
AiTokensThisMonth = 7, AiUsageMonthKey = monthKey
|
||||
});
|
||||
db.SaveChanges();
|
||||
|
||||
@ -192,7 +192,7 @@ namespace ManagerService.Tests.Controllers
|
||||
|
||||
var ok = Assert.IsType<OkObjectResult>(result);
|
||||
var dto = Assert.IsType<InstanceQuotaDTO>(ok.Value);
|
||||
Assert.Equal(7, dto.aiRequestsUsed);
|
||||
Assert.Equal(7, dto.aiTokensUsed);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
@ -202,7 +202,7 @@ namespace ManagerService.Tests.Controllers
|
||||
db.Instances.Add(new Instance
|
||||
{
|
||||
Id = "i1", Name = "Musée", DateCreation = DateTime.UtcNow,
|
||||
AiRequestsThisMonth = 7, AiUsageMonthKey = "2020-01"
|
||||
AiTokensThisMonth = 7, AiUsageMonthKey = "2020-01"
|
||||
});
|
||||
db.SaveChanges();
|
||||
|
||||
@ -210,7 +210,7 @@ namespace ManagerService.Tests.Controllers
|
||||
|
||||
var ok = Assert.IsType<OkObjectResult>(result);
|
||||
var dto = Assert.IsType<InstanceQuotaDTO>(ok.Value);
|
||||
Assert.Equal(0, dto.aiRequestsUsed);
|
||||
Assert.Equal(0, dto.aiTokensUsed);
|
||||
}
|
||||
|
||||
// ── DELETE ───────────────────────────────────────────────────────────
|
||||
|
||||
@ -38,7 +38,7 @@ namespace ManagerService.Tests.Controllers
|
||||
using var db = DbContextFactory.Create();
|
||||
db.SubscriptionPlans.Add(new SubscriptionPlan
|
||||
{
|
||||
Id = "p1", Name = "Starter", StorageQuotaBytes = 1024, AiRequestsPerMonth = 50
|
||||
Id = "p1", Name = "Starter", StorageQuotaBytes = 1024, AiTokensPerMonth = 50
|
||||
});
|
||||
db.SaveChanges();
|
||||
|
||||
@ -48,7 +48,7 @@ namespace ManagerService.Tests.Controllers
|
||||
var dto = Assert.IsType<SubscriptionPlanDTO>(ok.Value);
|
||||
Assert.Equal("Starter", dto.name);
|
||||
Assert.Equal(1024, dto.storageQuotaBytes);
|
||||
Assert.Equal(50, dto.aiRequestsPerMonth);
|
||||
Assert.Equal(50, dto.aiTokensPerMonth);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
@ -67,7 +67,7 @@ namespace ManagerService.Tests.Controllers
|
||||
public void Create_ValidDto_PersistsAndReturnsDto()
|
||||
{
|
||||
using var db = DbContextFactory.Create();
|
||||
var dto = new SubscriptionPlanDTO { name = "Premium", storageQuotaBytes = 5000, aiRequestsPerMonth = 100 };
|
||||
var dto = new SubscriptionPlanDTO { name = "Premium", storageQuotaBytes = 5000, aiTokensPerMonth = 100 };
|
||||
|
||||
var result = BuildController(db).Create(dto);
|
||||
|
||||
@ -97,7 +97,7 @@ namespace ManagerService.Tests.Controllers
|
||||
db.SubscriptionPlans.Add(new SubscriptionPlan { Id = "p1", Name = "Old" });
|
||||
db.SaveChanges();
|
||||
|
||||
var result = BuildController(db).Update(new SubscriptionPlanDTO { id = "p1", name = "New", storageQuotaBytes = 0, aiRequestsPerMonth = 0 });
|
||||
var result = BuildController(db).Update(new SubscriptionPlanDTO { id = "p1", name = "New", storageQuotaBytes = 0, aiTokensPerMonth = 0 });
|
||||
|
||||
Assert.IsType<OkObjectResult>(result);
|
||||
Assert.Equal("New", db.SubscriptionPlans.First().Name);
|
||||
|
||||
@ -58,18 +58,20 @@ namespace ManagerService.Controllers
|
||||
var monthKey = DateTime.UtcNow.ToString("yyyy-MM");
|
||||
if (instance.AiUsageMonthKey != monthKey)
|
||||
{
|
||||
instance.AiRequestsThisMonth = 0;
|
||||
instance.AiTokensThisMonth = 0;
|
||||
instance.AiUsageMonthKey = monthKey;
|
||||
_context.SaveChanges();
|
||||
}
|
||||
|
||||
var quota = instance.AiRequestsPerMonth;
|
||||
if (quota > 0 && instance.AiRequestsThisMonth >= quota)
|
||||
var quota = instance.AiTokensPerMonth;
|
||||
if (quota > 0 && instance.AiTokensThisMonth >= quota)
|
||||
return StatusCode(429, "Quota IA mensuel dépassé");
|
||||
|
||||
instance.AiRequestsThisMonth++;
|
||||
var result = await _assistantService.TranslateAsync(request);
|
||||
|
||||
instance.AiTokensThisMonth += result.TokensUsed;
|
||||
_context.SaveChanges();
|
||||
|
||||
var result = await _assistantService.TranslateAsync(request);
|
||||
return Ok(result);
|
||||
}
|
||||
catch (Exception ex)
|
||||
@ -110,18 +112,20 @@ namespace ManagerService.Controllers
|
||||
var monthKey = DateTime.UtcNow.ToString("yyyy-MM");
|
||||
if (instance.AiUsageMonthKey != monthKey)
|
||||
{
|
||||
instance.AiRequestsThisMonth = 0;
|
||||
instance.AiTokensThisMonth = 0;
|
||||
instance.AiUsageMonthKey = monthKey;
|
||||
_context.SaveChanges();
|
||||
}
|
||||
|
||||
var quota = instance.AiRequestsPerMonth;
|
||||
if (quota > 0 && instance.AiRequestsThisMonth >= quota)
|
||||
var quota = instance.AiTokensPerMonth;
|
||||
if (quota > 0 && instance.AiTokensThisMonth >= quota)
|
||||
return StatusCode(429, "Quota IA mensuel dépassé");
|
||||
|
||||
instance.AiRequestsThisMonth++;
|
||||
var result = await _assistantService.ChatAsync(request);
|
||||
|
||||
instance.AiTokensThisMonth += result.TokensUsed;
|
||||
_context.SaveChanges();
|
||||
|
||||
var result = await _assistantService.ChatAsync(request);
|
||||
return Ok(result);
|
||||
}
|
||||
catch (Exception ex)
|
||||
|
||||
@ -48,8 +48,8 @@ namespace ManagerService.Controllers
|
||||
/// <summary>
|
||||
/// Get a list of all configuration (summary)
|
||||
/// </summary>
|
||||
/// <param name="id">id instance</param>
|
||||
[Authorize(Policy = ManagerService.Service.Security.Policies.AppReadAccess)]
|
||||
/// <param name="id">id instance</param>
|
||||
[AllowAnonymous]
|
||||
[ProducesResponseType(typeof(List<ConfigurationDTO>), 200)]
|
||||
[ProducesResponseType(typeof(string), 500)]
|
||||
[HttpGet]
|
||||
@ -113,8 +113,8 @@ namespace ManagerService.Controllers
|
||||
/// <summary>
|
||||
/// Get a specific display configuration
|
||||
/// </summary>
|
||||
/// <param name="id">id configuration</param>
|
||||
[Authorize(Policy = ManagerService.Service.Security.Policies.AppReadAccess)]
|
||||
/// <param name="id">id configuration</param>
|
||||
[AllowAnonymous]
|
||||
[ProducesResponseType(typeof(ConfigurationDTO), 200)]
|
||||
[ProducesResponseType(typeof(string), 404)]
|
||||
[ProducesResponseType(typeof(string), 500)]
|
||||
|
||||
@ -123,7 +123,7 @@ namespace ManagerService.Controllers
|
||||
if (plan != null)
|
||||
{
|
||||
instance.StorageQuotaBytes = plan.StorageQuotaBytes;
|
||||
instance.AiRequestsPerMonth = plan.AiRequestsPerMonth;
|
||||
instance.AiTokensPerMonth = plan.AiTokensPerMonth;
|
||||
instance.HasStats = plan.HasStats;
|
||||
instance.StatsHistoryDays = plan.StatsHistoryDays;
|
||||
instance.HasAdvancedStats = plan.HasAdvancedStats;
|
||||
@ -338,10 +338,10 @@ namespace ManagerService.Controllers
|
||||
.Sum(r => (long?)r.SizeBytes) ?? 0;
|
||||
|
||||
var monthKey = DateTime.UtcNow.ToString("yyyy-MM");
|
||||
var aiUsed = instance.AiUsageMonthKey == monthKey ? instance.AiRequestsThisMonth : 0;
|
||||
var aiUsed = instance.AiUsageMonthKey == monthKey ? instance.AiTokensThisMonth : 0;
|
||||
|
||||
var storageQuota = instance.StorageQuotaBytes;
|
||||
var aiQuota = instance.AiRequestsPerMonth;
|
||||
var aiQuota = instance.AiTokensPerMonth;
|
||||
|
||||
if ((storageQuota == 0 || aiQuota == 0) && instance.SubscriptionPlanId != null)
|
||||
{
|
||||
@ -349,7 +349,7 @@ namespace ManagerService.Controllers
|
||||
if (plan != null)
|
||||
{
|
||||
if (storageQuota == 0) storageQuota = plan.StorageQuotaBytes;
|
||||
if (aiQuota == 0) aiQuota = plan.AiRequestsPerMonth;
|
||||
if (aiQuota == 0) aiQuota = plan.AiTokensPerMonth;
|
||||
}
|
||||
}
|
||||
|
||||
@ -357,8 +357,8 @@ namespace ManagerService.Controllers
|
||||
{
|
||||
storageUsedBytes = storageUsed,
|
||||
storageQuotaBytes = storageQuota,
|
||||
aiRequestsUsed = aiUsed,
|
||||
aiRequestsPerMonth = aiQuota
|
||||
aiTokensUsed = aiUsed,
|
||||
aiTokensPerMonth = aiQuota
|
||||
});
|
||||
}
|
||||
catch (Exception ex)
|
||||
|
||||
@ -851,7 +851,6 @@ namespace ManagerService.Controllers
|
||||
[HttpPut("order")]
|
||||
public ObjectResult UpdateOrder([FromBody] List<SectionDTO> updatedSectionsOrder)
|
||||
{
|
||||
// TODO REWRITE LOGIC..
|
||||
try
|
||||
{
|
||||
if (updatedSectionsOrder == null)
|
||||
@ -864,16 +863,42 @@ namespace ManagerService.Controllers
|
||||
throw new KeyNotFoundException($"Section {section.label} with id {section.id} does not exist");
|
||||
}
|
||||
|
||||
foreach (var updatedSection in updatedSectionsOrder)
|
||||
// The client may only send a subset of the configuration's sections (e.g. a filtered
|
||||
// reorder view) — renormalize against the full top-level set so `Order` always stays
|
||||
// 0-based and contiguous, instead of blindly writing the payload's values.
|
||||
foreach (var configurationId in updatedSectionsOrder.Select(s => s.configurationId).Distinct())
|
||||
{
|
||||
var section = _myInfoMateDbContext.Sections.FirstOrDefault(s => s.Id == updatedSection.id);
|
||||
//OldSection section = _sectionService.GetById(updatedSection.id);
|
||||
section.Order = updatedSection.order.GetValueOrDefault();
|
||||
_myInfoMateDbContext.SaveChanges();
|
||||
var baseline = _myInfoMateDbContext.Sections
|
||||
.Where(s => s.ConfigurationId == configurationId && !s.IsSubSection)
|
||||
.OrderBy(s => s.Order)
|
||||
.ToList();
|
||||
|
||||
//_sectionService.Update(section.Id, section);
|
||||
var includedIds = updatedSectionsOrder
|
||||
.Where(s => s.configurationId == configurationId)
|
||||
.Select(s => s.id)
|
||||
.ToHashSet();
|
||||
|
||||
var newSubsequence = updatedSectionsOrder
|
||||
.Where(s => s.configurationId == configurationId)
|
||||
.OrderBy(s => s.order.GetValueOrDefault())
|
||||
.Select(s => baseline.First(b => b.Id == s.id))
|
||||
.ToList();
|
||||
|
||||
var slots = baseline
|
||||
.Select((section, index) => (section, index))
|
||||
.Where(t => includedIds.Contains(t.section.Id))
|
||||
.Select(t => t.index)
|
||||
.ToList();
|
||||
|
||||
for (int k = 0; k < slots.Count; k++)
|
||||
baseline[slots[k]] = newSubsequence[k];
|
||||
|
||||
for (int i = 0; i < baseline.Count; i++)
|
||||
baseline[i].Order = i;
|
||||
}
|
||||
|
||||
_myInfoMateDbContext.SaveChanges();
|
||||
|
||||
if (updatedSectionsOrder.Count > 0) {
|
||||
MqttClientService.PublishMessage($"config/{updatedSectionsOrder[0].configurationId}", JsonConvert.SerializeObject(new PlayerMessageDTO() { configChanged = true }));
|
||||
}
|
||||
@ -975,7 +1000,7 @@ namespace ManagerService.Controllers
|
||||
var configuration = _myInfoMateDbContext.Configurations.FirstOrDefault(c => c.Id == section.ConfigurationId);
|
||||
|
||||
List<Section> sections = _myInfoMateDbContext.Sections.Where(s => s.ConfigurationId == section.ConfigurationId && !s.IsSubSection).ToList();
|
||||
int i = 1;
|
||||
int i = 0;
|
||||
List<Section> orderedSection = sections.OrderBy(s => s.Order).ToList();
|
||||
foreach (var sectionDb in orderedSection)
|
||||
{
|
||||
|
||||
@ -50,6 +50,7 @@ namespace ManagerService.DTOs
|
||||
/// Le front peut désactiver l'écoute automatique après le TTS.
|
||||
/// </summary>
|
||||
public bool ExpectsReply { get; set; } = true;
|
||||
public long TokensUsed { get; set; }
|
||||
}
|
||||
|
||||
public class AiTranslateRequest
|
||||
@ -62,5 +63,6 @@ namespace ManagerService.DTOs
|
||||
public class AiTranslateResponse
|
||||
{
|
||||
public Dictionary<string, string> Translations { get; set; } = new();
|
||||
public long TokensUsed { get; set; }
|
||||
}
|
||||
}
|
||||
|
||||
@ -23,10 +23,10 @@ namespace ManagerService.DTOs
|
||||
|
||||
public string? subscriptionPlanId { get; set; }
|
||||
public SubscriptionPlanDTO? subscriptionPlan { get; set; }
|
||||
public int? aiRequestsThisMonth { get; set; }
|
||||
public long? aiTokensThisMonth { get; set; }
|
||||
public string? aiUsageMonthKey { get; set; }
|
||||
public long? storageQuotaBytes { get; set; }
|
||||
public int? aiRequestsPerMonth { get; set; }
|
||||
public long? aiTokensPerMonth { get; set; }
|
||||
public bool? hasStats { get; set; }
|
||||
public int? statsHistoryDays { get; set; }
|
||||
public bool? hasAdvancedStats { get; set; }
|
||||
|
||||
@ -4,7 +4,7 @@ namespace ManagerService.DTOs
|
||||
{
|
||||
public long storageUsedBytes { get; set; }
|
||||
public long storageQuotaBytes { get; set; }
|
||||
public int aiRequestsUsed { get; set; }
|
||||
public int aiRequestsPerMonth { get; set; }
|
||||
public long aiTokensUsed { get; set; }
|
||||
public long aiTokensPerMonth { get; set; }
|
||||
}
|
||||
}
|
||||
|
||||
@ -5,7 +5,7 @@ namespace ManagerService.DTOs
|
||||
public string? id { get; set; }
|
||||
public string name { get; set; }
|
||||
public long storageQuotaBytes { get; set; }
|
||||
public int aiRequestsPerMonth { get; set; }
|
||||
public long aiTokensPerMonth { get; set; }
|
||||
public bool hasStats { get; set; }
|
||||
public int statsHistoryDays { get; set; }
|
||||
public bool hasAdvancedStats { get; set; }
|
||||
|
||||
@ -3,7 +3,7 @@ using System.ComponentModel.DataAnnotations;
|
||||
|
||||
namespace ManagerService.Data
|
||||
{
|
||||
public class ApiKey
|
||||
public class ApiKey : IAuditableEntity
|
||||
{
|
||||
[Key]
|
||||
public string Id { get; set; }
|
||||
@ -28,6 +28,8 @@ namespace ManagerService.Data
|
||||
|
||||
public DateTime DateCreation { get; set; }
|
||||
|
||||
public DateTime DateUpdate { get; set; }
|
||||
|
||||
public DateTime? DateExpiration { get; set; }
|
||||
}
|
||||
}
|
||||
|
||||
@ -13,11 +13,15 @@ namespace ManagerService.Data
|
||||
/// application instances, optionally with an order or active status.
|
||||
/// Useful for managing configuration assignment and display logic per app type.
|
||||
/// </summary>
|
||||
public class AppConfigurationLink
|
||||
public class AppConfigurationLink : IAuditableEntity
|
||||
{
|
||||
[Key]
|
||||
public string Id { get; set; }
|
||||
|
||||
public DateTime DateCreation { get; set; }
|
||||
|
||||
public DateTime DateUpdate { get; set; }
|
||||
|
||||
[Required]
|
||||
public string ConfigurationId { get; set; }
|
||||
|
||||
|
||||
@ -2,18 +2,21 @@
|
||||
using ManagerService.Data.SubSection;
|
||||
using ManagerService.DTOs;
|
||||
using ManagerService.Services;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
using System.ComponentModel.DataAnnotations.Schema;
|
||||
using System.Linq;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
|
||||
namespace ManagerService.Data
|
||||
{
|
||||
/// <summary>
|
||||
/// Defines the link between an application instance and a configuration,
|
||||
/// Defines the link between an application instance and a configuration,
|
||||
/// allowing apps to use one or multiple configurations.
|
||||
/// </summary>
|
||||
public class ApplicationInstance
|
||||
[Index(nameof(InstanceId))]
|
||||
public class ApplicationInstance : IAuditableEntity
|
||||
{
|
||||
[Key]
|
||||
public string Id { get; set; }
|
||||
@ -21,6 +24,10 @@ namespace ManagerService.Data
|
||||
[Required]
|
||||
public string InstanceId { get; set; }
|
||||
|
||||
public DateTime DateCreation { get; set; }
|
||||
|
||||
public DateTime DateUpdate { get; set; }
|
||||
|
||||
[Required]
|
||||
public AppType AppType { get; set; }
|
||||
|
||||
|
||||
@ -1,7 +1,9 @@
|
||||
using System;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
|
||||
namespace ManagerService.Data
|
||||
{
|
||||
[Index(nameof(InstanceId))]
|
||||
public class AuditLog
|
||||
{
|
||||
public string Id { get; set; } = Guid.NewGuid().ToString();
|
||||
|
||||
@ -5,13 +5,15 @@ using System.ComponentModel.DataAnnotations;
|
||||
using System.ComponentModel.DataAnnotations.Schema;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
|
||||
namespace ManagerService.Data
|
||||
{
|
||||
/// <summary>
|
||||
/// Configuration Information
|
||||
/// </summary>
|
||||
public class Configuration
|
||||
[Index(nameof(InstanceId))]
|
||||
public class Configuration : IAuditableEntity
|
||||
{
|
||||
[Key]
|
||||
[Required]
|
||||
@ -39,6 +41,8 @@ namespace ManagerService.Data
|
||||
|
||||
public DateTime DateCreation { get; set; }
|
||||
|
||||
public DateTime DateUpdate { get; set; }
|
||||
|
||||
public bool IsOffline { get; set; }
|
||||
|
||||
public string LoaderImageId { get; set; } // Config can have their specific loader if needed
|
||||
|
||||
@ -2,13 +2,15 @@
|
||||
using System;
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
using System.ComponentModel.DataAnnotations.Schema;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
|
||||
namespace ManagerService.Data
|
||||
{
|
||||
/// <summary>
|
||||
/// Device Information (Tablet)
|
||||
/// </summary>
|
||||
public class Device
|
||||
[Index(nameof(InstanceId))]
|
||||
public class Device : IAuditableEntity
|
||||
{
|
||||
[Key]
|
||||
[Required]
|
||||
|
||||
10
ManagerService/Data/IAuditableEntity.cs
Normal file
10
ManagerService/Data/IAuditableEntity.cs
Normal file
@ -0,0 +1,10 @@
|
||||
using System;
|
||||
|
||||
namespace ManagerService.Data
|
||||
{
|
||||
public interface IAuditableEntity
|
||||
{
|
||||
DateTime DateCreation { get; set; }
|
||||
DateTime DateUpdate { get; set; }
|
||||
}
|
||||
}
|
||||
@ -9,7 +9,7 @@ namespace ManagerService.Data
|
||||
/// <summary>
|
||||
/// Instance Information
|
||||
/// </summary>
|
||||
public class Instance
|
||||
public class Instance : IAuditableEntity
|
||||
{
|
||||
[Key]
|
||||
[Required]
|
||||
@ -20,6 +20,8 @@ namespace ManagerService.Data
|
||||
|
||||
public DateTime DateCreation { get; set; }
|
||||
|
||||
public DateTime DateUpdate { get; set; }
|
||||
|
||||
public string PinCode { get; set; }
|
||||
|
||||
public bool IsPushNotification { get; set; }
|
||||
@ -43,13 +45,13 @@ namespace ManagerService.Data
|
||||
[ForeignKey("SubscriptionPlanId")]
|
||||
public SubscriptionPlan? SubscriptionPlan { get; set; }
|
||||
|
||||
public int AiRequestsThisMonth { get; set; } = 0;
|
||||
public long AiTokensThisMonth { get; set; } = 0;
|
||||
|
||||
public string AiUsageMonthKey { get; set; } = "";
|
||||
|
||||
public long StorageQuotaBytes { get; set; } = 0;
|
||||
|
||||
public int AiRequestsPerMonth { get; set; } = 0;
|
||||
public long AiTokensPerMonth { get; set; } = 0;
|
||||
|
||||
public bool HasStats { get; set; } = false;
|
||||
|
||||
@ -76,10 +78,10 @@ namespace ManagerService.Data
|
||||
publicApiKey = PublicApiKey,
|
||||
subscriptionPlanId = SubscriptionPlanId,
|
||||
subscriptionPlan = SubscriptionPlan?.ToDTO(),
|
||||
aiRequestsThisMonth = AiRequestsThisMonth,
|
||||
aiTokensThisMonth = AiTokensThisMonth,
|
||||
aiUsageMonthKey = AiUsageMonthKey,
|
||||
storageQuotaBytes = StorageQuotaBytes,
|
||||
aiRequestsPerMonth = AiRequestsPerMonth,
|
||||
aiTokensPerMonth = AiTokensPerMonth,
|
||||
hasStats = HasStats,
|
||||
statsHistoryDays = StatsHistoryDays,
|
||||
hasAdvancedStats = HasAdvancedStats,
|
||||
@ -106,8 +108,8 @@ namespace ManagerService.Data
|
||||
SubscriptionPlanId = instanceDTO.subscriptionPlanId;
|
||||
if (instanceDTO.storageQuotaBytes.HasValue)
|
||||
StorageQuotaBytes = instanceDTO.storageQuotaBytes.Value;
|
||||
if (instanceDTO.aiRequestsPerMonth.HasValue)
|
||||
AiRequestsPerMonth = instanceDTO.aiRequestsPerMonth.Value;
|
||||
if (instanceDTO.aiTokensPerMonth.HasValue)
|
||||
AiTokensPerMonth = instanceDTO.aiTokensPerMonth.Value;
|
||||
if (instanceDTO.hasStats.HasValue)
|
||||
HasStats = instanceDTO.hasStats.Value;
|
||||
if (instanceDTO.statsHistoryDays.HasValue)
|
||||
|
||||
@ -64,8 +64,19 @@ namespace ManagerService.Data
|
||||
// Audit
|
||||
public DbSet<AuditLog> AuditLogs { get; set; }
|
||||
|
||||
public override int SaveChanges(bool acceptAllChangesOnSuccess)
|
||||
{
|
||||
StampAuditableEntities();
|
||||
var auditEntries = BuildAuditEntries();
|
||||
var result = base.SaveChanges(acceptAllChangesOnSuccess);
|
||||
if (auditEntries.Any())
|
||||
base.SaveChanges(acceptAllChangesOnSuccess);
|
||||
return result;
|
||||
}
|
||||
|
||||
public override async Task<int> SaveChangesAsync(CancellationToken cancellationToken = default)
|
||||
{
|
||||
StampAuditableEntities();
|
||||
var auditEntries = BuildAuditEntries();
|
||||
var result = await base.SaveChangesAsync(cancellationToken);
|
||||
if (auditEntries.Any())
|
||||
@ -73,6 +84,18 @@ namespace ManagerService.Data
|
||||
return result;
|
||||
}
|
||||
|
||||
private void StampAuditableEntities()
|
||||
{
|
||||
var now = DateTime.UtcNow;
|
||||
foreach (var entry in ChangeTracker.Entries<IAuditableEntity>())
|
||||
{
|
||||
if (entry.State == EntityState.Added)
|
||||
entry.Entity.DateCreation = now;
|
||||
if (entry.State is EntityState.Added or EntityState.Modified)
|
||||
entry.Entity.DateUpdate = now;
|
||||
}
|
||||
}
|
||||
|
||||
private static readonly HashSet<Type> AuditedTypes = new()
|
||||
{
|
||||
typeof(Section), typeof(Resource), typeof(Configuration),
|
||||
@ -399,7 +422,7 @@ namespace ManagerService.Data
|
||||
modelBuilder.Entity<Instance>()
|
||||
.Property(i => i.StorageQuotaBytes).ValueGeneratedNever();
|
||||
modelBuilder.Entity<Instance>()
|
||||
.Property(i => i.AiRequestsPerMonth).ValueGeneratedNever();
|
||||
.Property(i => i.AiTokensPerMonth).ValueGeneratedNever();
|
||||
modelBuilder.Entity<Instance>()
|
||||
.Property(i => i.HasStats).ValueGeneratedNever();
|
||||
modelBuilder.Entity<Instance>()
|
||||
@ -414,7 +437,7 @@ namespace ManagerService.Data
|
||||
Id = "plan-starter",
|
||||
Name = "Starter",
|
||||
StorageQuotaBytes = 1L * 1024 * 1024 * 1024, // 1 GB
|
||||
AiRequestsPerMonth = 0,
|
||||
AiTokensPerMonth = 0,
|
||||
HasStats = false,
|
||||
StatsHistoryDays = 30,
|
||||
HasAdvancedStats = false,
|
||||
@ -424,7 +447,7 @@ namespace ManagerService.Data
|
||||
Id = "plan-standard",
|
||||
Name = "Standard",
|
||||
StorageQuotaBytes = 10L * 1024 * 1024 * 1024, // 10 GB
|
||||
AiRequestsPerMonth = 500,
|
||||
AiTokensPerMonth = 5_000_000, // ~500 req/mois * ~10k tokens/req estimés, à ajuster selon l'usage réel observé
|
||||
HasStats = true,
|
||||
StatsHistoryDays = 30,
|
||||
HasAdvancedStats = false,
|
||||
@ -434,7 +457,7 @@ namespace ManagerService.Data
|
||||
Id = "plan-premium",
|
||||
Name = "Premium",
|
||||
StorageQuotaBytes = 50L * 1024 * 1024 * 1024, // 50 GB
|
||||
AiRequestsPerMonth = 2000,
|
||||
AiTokensPerMonth = 20_000_000, // ~2000 req/mois * ~10k tokens/req estimés, à ajuster selon l'usage réel observé
|
||||
HasStats = true,
|
||||
StatsHistoryDays = 0,
|
||||
HasAdvancedStats = true,
|
||||
|
||||
@ -1,13 +1,15 @@
|
||||
using ManagerService.DTOs;
|
||||
using System;
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
|
||||
namespace ManagerService.Data
|
||||
{
|
||||
/// <summary>
|
||||
/// Resource Information
|
||||
/// </summary>
|
||||
public class Resource
|
||||
[Index(nameof(InstanceId))]
|
||||
public class Resource : IAuditableEntity
|
||||
{
|
||||
[Key]
|
||||
[Required]
|
||||
@ -28,6 +30,8 @@ namespace ManagerService.Data
|
||||
/*[BsonElement("DateCreation")]*/
|
||||
public DateTime DateCreation { get; set; }
|
||||
|
||||
public DateTime DateUpdate { get; set; }
|
||||
|
||||
/*[BsonElement("InstanceId")]
|
||||
[BsonRequired]*/
|
||||
[Required]
|
||||
|
||||
@ -3,6 +3,7 @@ using System;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
using System.ComponentModel.DataAnnotations.Schema;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
|
||||
|
||||
namespace ManagerService.Data
|
||||
@ -10,7 +11,8 @@ namespace ManagerService.Data
|
||||
/// <summary>
|
||||
/// Section Information
|
||||
/// </summary>
|
||||
public class Section
|
||||
[Index(nameof(InstanceId))]
|
||||
public class Section : IAuditableEntity
|
||||
{
|
||||
[Key]
|
||||
[Required]
|
||||
@ -45,6 +47,8 @@ namespace ManagerService.Data
|
||||
|
||||
public DateTime DateCreation { get; set; }
|
||||
|
||||
public DateTime DateUpdate { get; set; }
|
||||
|
||||
[Required]
|
||||
public string InstanceId { get; set; }
|
||||
|
||||
|
||||
@ -1,13 +1,16 @@
|
||||
using ManagerService.Data;
|
||||
using ManagerService.DTOs;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel.DataAnnotations.Schema;
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
using System.Linq;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
|
||||
namespace ManagerService.Data.SubSection
|
||||
{
|
||||
public class GuidedPath // Parcours
|
||||
[Index(nameof(InstanceId))]
|
||||
public class GuidedPath : IAuditableEntity // Parcours
|
||||
{
|
||||
[Key]
|
||||
public string Id { get; set; }
|
||||
@ -15,6 +18,10 @@ namespace ManagerService.Data.SubSection
|
||||
[Required]
|
||||
public string InstanceId { get; set; }
|
||||
|
||||
public DateTime DateCreation { get; set; }
|
||||
|
||||
public DateTime DateUpdate { get; set; }
|
||||
|
||||
[Required]
|
||||
[Column(TypeName = "jsonb")]
|
||||
public List<TranslationDTO> Title { get; set; }
|
||||
|
||||
@ -14,7 +14,7 @@ namespace ManagerService.Data
|
||||
|
||||
public long StorageQuotaBytes { get; set; } = 0;
|
||||
|
||||
public int AiRequestsPerMonth { get; set; } = 0;
|
||||
public long AiTokensPerMonth { get; set; } = 0;
|
||||
|
||||
/// <summary>Whether the plan includes any stats at all.</summary>
|
||||
public bool HasStats { get; set; } = false;
|
||||
@ -32,7 +32,7 @@ namespace ManagerService.Data
|
||||
id = Id,
|
||||
name = Name,
|
||||
storageQuotaBytes = StorageQuotaBytes,
|
||||
aiRequestsPerMonth = AiRequestsPerMonth,
|
||||
aiTokensPerMonth = AiTokensPerMonth,
|
||||
hasStats = HasStats,
|
||||
statsHistoryDays = StatsHistoryDays,
|
||||
hasAdvancedStats = HasAdvancedStats,
|
||||
@ -43,7 +43,7 @@ namespace ManagerService.Data
|
||||
{
|
||||
Name = dto.name;
|
||||
StorageQuotaBytes = dto.storageQuotaBytes;
|
||||
AiRequestsPerMonth = dto.aiRequestsPerMonth;
|
||||
AiTokensPerMonth = dto.aiTokensPerMonth;
|
||||
HasStats = dto.hasStats;
|
||||
StatsHistoryDays = dto.statsHistoryDays;
|
||||
HasAdvancedStats = dto.hasAdvancedStats;
|
||||
|
||||
@ -1,13 +1,15 @@
|
||||
using ManagerService.DTOs;
|
||||
using System;
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
|
||||
namespace ManagerService.Data
|
||||
{
|
||||
/// <summary>
|
||||
/// User Information
|
||||
/// </summary>
|
||||
public class User
|
||||
[Index(nameof(InstanceId))]
|
||||
public class User : IAuditableEntity
|
||||
{
|
||||
[Key]
|
||||
[Required]
|
||||
@ -42,6 +44,8 @@ namespace ManagerService.Data
|
||||
//[BsonElement("DateCreation")]
|
||||
public DateTime DateCreation { get; set; }
|
||||
|
||||
public DateTime DateUpdate { get; set; }
|
||||
|
||||
/*[BsonElement("InstanceId")]
|
||||
[BsonRequired]*/
|
||||
[Required]
|
||||
|
||||
1647
ManagerService/Migrations/20260715143705_AddInstanceIdIndexes.Designer.cs
generated
Normal file
1647
ManagerService/Migrations/20260715143705_AddInstanceIdIndexes.Designer.cs
generated
Normal file
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,90 @@
|
||||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
|
||||
#nullable disable
|
||||
|
||||
namespace ManagerService.Migrations
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public partial class AddInstanceIdIndexes : Migration
|
||||
{
|
||||
/// <inheritdoc />
|
||||
protected override void Up(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "IX_Users_InstanceId",
|
||||
table: "Users",
|
||||
column: "InstanceId");
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "IX_Sections_InstanceId",
|
||||
table: "Sections",
|
||||
column: "InstanceId");
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "IX_Resources_InstanceId",
|
||||
table: "Resources",
|
||||
column: "InstanceId");
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "IX_GuidedPaths_InstanceId",
|
||||
table: "GuidedPaths",
|
||||
column: "InstanceId");
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "IX_Devices_InstanceId",
|
||||
table: "Devices",
|
||||
column: "InstanceId");
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "IX_Configurations_InstanceId",
|
||||
table: "Configurations",
|
||||
column: "InstanceId");
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "IX_AuditLogs_InstanceId",
|
||||
table: "AuditLogs",
|
||||
column: "InstanceId");
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "IX_ApplicationInstances_InstanceId",
|
||||
table: "ApplicationInstances",
|
||||
column: "InstanceId");
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void Down(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.DropIndex(
|
||||
name: "IX_Users_InstanceId",
|
||||
table: "Users");
|
||||
|
||||
migrationBuilder.DropIndex(
|
||||
name: "IX_Sections_InstanceId",
|
||||
table: "Sections");
|
||||
|
||||
migrationBuilder.DropIndex(
|
||||
name: "IX_Resources_InstanceId",
|
||||
table: "Resources");
|
||||
|
||||
migrationBuilder.DropIndex(
|
||||
name: "IX_GuidedPaths_InstanceId",
|
||||
table: "GuidedPaths");
|
||||
|
||||
migrationBuilder.DropIndex(
|
||||
name: "IX_Devices_InstanceId",
|
||||
table: "Devices");
|
||||
|
||||
migrationBuilder.DropIndex(
|
||||
name: "IX_Configurations_InstanceId",
|
||||
table: "Configurations");
|
||||
|
||||
migrationBuilder.DropIndex(
|
||||
name: "IX_AuditLogs_InstanceId",
|
||||
table: "AuditLogs");
|
||||
|
||||
migrationBuilder.DropIndex(
|
||||
name: "IX_ApplicationInstances_InstanceId",
|
||||
table: "ApplicationInstances");
|
||||
}
|
||||
}
|
||||
}
|
||||
1683
ManagerService/Migrations/20260715144504_AddDateUpdateAudit.Designer.cs
generated
Normal file
1683
ManagerService/Migrations/20260715144504_AddDateUpdateAudit.Designer.cs
generated
Normal file
File diff suppressed because it is too large
Load Diff
165
ManagerService/Migrations/20260715144504_AddDateUpdateAudit.cs
Normal file
165
ManagerService/Migrations/20260715144504_AddDateUpdateAudit.cs
Normal file
@ -0,0 +1,165 @@
|
||||
using System;
|
||||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
|
||||
#nullable disable
|
||||
|
||||
namespace ManagerService.Migrations
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public partial class AddDateUpdateAudit : Migration
|
||||
{
|
||||
/// <inheritdoc />
|
||||
protected override void Up(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.AddColumn<DateTime>(
|
||||
name: "DateUpdate",
|
||||
table: "Users",
|
||||
type: "timestamp with time zone",
|
||||
nullable: false,
|
||||
defaultValue: new DateTime(1, 1, 1, 0, 0, 0, 0, DateTimeKind.Unspecified));
|
||||
|
||||
migrationBuilder.AddColumn<DateTime>(
|
||||
name: "DateUpdate",
|
||||
table: "Sections",
|
||||
type: "timestamp with time zone",
|
||||
nullable: false,
|
||||
defaultValue: new DateTime(1, 1, 1, 0, 0, 0, 0, DateTimeKind.Unspecified));
|
||||
|
||||
migrationBuilder.AddColumn<DateTime>(
|
||||
name: "DateUpdate",
|
||||
table: "Resources",
|
||||
type: "timestamp with time zone",
|
||||
nullable: false,
|
||||
defaultValue: new DateTime(1, 1, 1, 0, 0, 0, 0, DateTimeKind.Unspecified));
|
||||
|
||||
migrationBuilder.AddColumn<DateTime>(
|
||||
name: "DateUpdate",
|
||||
table: "Instances",
|
||||
type: "timestamp with time zone",
|
||||
nullable: false,
|
||||
defaultValue: new DateTime(1, 1, 1, 0, 0, 0, 0, DateTimeKind.Unspecified));
|
||||
|
||||
migrationBuilder.AddColumn<DateTime>(
|
||||
name: "DateCreation",
|
||||
table: "GuidedPaths",
|
||||
type: "timestamp with time zone",
|
||||
nullable: false,
|
||||
defaultValue: new DateTime(1, 1, 1, 0, 0, 0, 0, DateTimeKind.Unspecified));
|
||||
|
||||
migrationBuilder.AddColumn<DateTime>(
|
||||
name: "DateUpdate",
|
||||
table: "GuidedPaths",
|
||||
type: "timestamp with time zone",
|
||||
nullable: false,
|
||||
defaultValue: new DateTime(1, 1, 1, 0, 0, 0, 0, DateTimeKind.Unspecified));
|
||||
|
||||
migrationBuilder.AddColumn<DateTime>(
|
||||
name: "DateUpdate",
|
||||
table: "Configurations",
|
||||
type: "timestamp with time zone",
|
||||
nullable: false,
|
||||
defaultValue: new DateTime(1, 1, 1, 0, 0, 0, 0, DateTimeKind.Unspecified));
|
||||
|
||||
migrationBuilder.AddColumn<DateTime>(
|
||||
name: "DateCreation",
|
||||
table: "ApplicationInstances",
|
||||
type: "timestamp with time zone",
|
||||
nullable: false,
|
||||
defaultValue: new DateTime(1, 1, 1, 0, 0, 0, 0, DateTimeKind.Unspecified));
|
||||
|
||||
migrationBuilder.AddColumn<DateTime>(
|
||||
name: "DateUpdate",
|
||||
table: "ApplicationInstances",
|
||||
type: "timestamp with time zone",
|
||||
nullable: false,
|
||||
defaultValue: new DateTime(1, 1, 1, 0, 0, 0, 0, DateTimeKind.Unspecified));
|
||||
|
||||
migrationBuilder.AddColumn<DateTime>(
|
||||
name: "DateCreation",
|
||||
table: "AppConfigurationLinks",
|
||||
type: "timestamp with time zone",
|
||||
nullable: false,
|
||||
defaultValue: new DateTime(1, 1, 1, 0, 0, 0, 0, DateTimeKind.Unspecified));
|
||||
|
||||
migrationBuilder.AddColumn<DateTime>(
|
||||
name: "DateUpdate",
|
||||
table: "AppConfigurationLinks",
|
||||
type: "timestamp with time zone",
|
||||
nullable: false,
|
||||
defaultValue: new DateTime(1, 1, 1, 0, 0, 0, 0, DateTimeKind.Unspecified));
|
||||
|
||||
migrationBuilder.AddColumn<DateTime>(
|
||||
name: "DateUpdate",
|
||||
table: "ApiKeys",
|
||||
type: "timestamp with time zone",
|
||||
nullable: false,
|
||||
defaultValue: new DateTime(1, 1, 1, 0, 0, 0, 0, DateTimeKind.Unspecified));
|
||||
|
||||
// Backfill : pour les lignes existantes, DateUpdate reprend la DateCreation connue.
|
||||
migrationBuilder.Sql("UPDATE \"Users\" SET \"DateUpdate\" = \"DateCreation\";");
|
||||
migrationBuilder.Sql("UPDATE \"Sections\" SET \"DateUpdate\" = \"DateCreation\";");
|
||||
migrationBuilder.Sql("UPDATE \"Resources\" SET \"DateUpdate\" = \"DateCreation\";");
|
||||
migrationBuilder.Sql("UPDATE \"Instances\" SET \"DateUpdate\" = \"DateCreation\";");
|
||||
migrationBuilder.Sql("UPDATE \"Configurations\" SET \"DateUpdate\" = \"DateCreation\";");
|
||||
migrationBuilder.Sql("UPDATE \"ApiKeys\" SET \"DateUpdate\" = \"DateCreation\";");
|
||||
|
||||
// GuidedPaths/ApplicationInstances/AppConfigurationLinks n'avaient aucune date avant cette migration :
|
||||
// on ne peut pas reconstituer la vraie date de création, donc on initialise les deux au moment du backfill.
|
||||
migrationBuilder.Sql("UPDATE \"GuidedPaths\" SET \"DateCreation\" = now(), \"DateUpdate\" = now();");
|
||||
migrationBuilder.Sql("UPDATE \"ApplicationInstances\" SET \"DateCreation\" = now(), \"DateUpdate\" = now();");
|
||||
migrationBuilder.Sql("UPDATE \"AppConfigurationLinks\" SET \"DateCreation\" = now(), \"DateUpdate\" = now();");
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void Down(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.DropColumn(
|
||||
name: "DateUpdate",
|
||||
table: "Users");
|
||||
|
||||
migrationBuilder.DropColumn(
|
||||
name: "DateUpdate",
|
||||
table: "Sections");
|
||||
|
||||
migrationBuilder.DropColumn(
|
||||
name: "DateUpdate",
|
||||
table: "Resources");
|
||||
|
||||
migrationBuilder.DropColumn(
|
||||
name: "DateUpdate",
|
||||
table: "Instances");
|
||||
|
||||
migrationBuilder.DropColumn(
|
||||
name: "DateCreation",
|
||||
table: "GuidedPaths");
|
||||
|
||||
migrationBuilder.DropColumn(
|
||||
name: "DateUpdate",
|
||||
table: "GuidedPaths");
|
||||
|
||||
migrationBuilder.DropColumn(
|
||||
name: "DateUpdate",
|
||||
table: "Configurations");
|
||||
|
||||
migrationBuilder.DropColumn(
|
||||
name: "DateCreation",
|
||||
table: "ApplicationInstances");
|
||||
|
||||
migrationBuilder.DropColumn(
|
||||
name: "DateUpdate",
|
||||
table: "ApplicationInstances");
|
||||
|
||||
migrationBuilder.DropColumn(
|
||||
name: "DateCreation",
|
||||
table: "AppConfigurationLinks");
|
||||
|
||||
migrationBuilder.DropColumn(
|
||||
name: "DateUpdate",
|
||||
table: "AppConfigurationLinks");
|
||||
|
||||
migrationBuilder.DropColumn(
|
||||
name: "DateUpdate",
|
||||
table: "ApiKeys");
|
||||
}
|
||||
}
|
||||
}
|
||||
1683
ManagerService/Migrations/20260717121501_AiQuotaTokensInsteadOfRequests.Designer.cs
generated
Normal file
1683
ManagerService/Migrations/20260717121501_AiQuotaTokensInsteadOfRequests.Designer.cs
generated
Normal file
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,126 @@
|
||||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
|
||||
#nullable disable
|
||||
|
||||
namespace ManagerService.Migrations
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public partial class AiQuotaTokensInsteadOfRequests : Migration
|
||||
{
|
||||
/// <inheritdoc />
|
||||
protected override void Up(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.DropColumn(
|
||||
name: "AiRequestsPerMonth",
|
||||
table: "SubscriptionPlans");
|
||||
|
||||
migrationBuilder.DropColumn(
|
||||
name: "AiRequestsPerMonth",
|
||||
table: "Instances");
|
||||
|
||||
migrationBuilder.DropColumn(
|
||||
name: "AiRequestsThisMonth",
|
||||
table: "Instances");
|
||||
|
||||
migrationBuilder.AddColumn<long>(
|
||||
name: "AiTokensPerMonth",
|
||||
table: "SubscriptionPlans",
|
||||
type: "bigint",
|
||||
nullable: false,
|
||||
defaultValue: 0L);
|
||||
|
||||
migrationBuilder.AddColumn<long>(
|
||||
name: "AiTokensPerMonth",
|
||||
table: "Instances",
|
||||
type: "bigint",
|
||||
nullable: false,
|
||||
defaultValue: 0L);
|
||||
|
||||
migrationBuilder.AddColumn<long>(
|
||||
name: "AiTokensThisMonth",
|
||||
table: "Instances",
|
||||
type: "bigint",
|
||||
nullable: false,
|
||||
defaultValue: 0L);
|
||||
|
||||
migrationBuilder.UpdateData(
|
||||
table: "SubscriptionPlans",
|
||||
keyColumn: "Id",
|
||||
keyValue: "plan-premium",
|
||||
column: "AiTokensPerMonth",
|
||||
value: 20000000L);
|
||||
|
||||
migrationBuilder.UpdateData(
|
||||
table: "SubscriptionPlans",
|
||||
keyColumn: "Id",
|
||||
keyValue: "plan-standard",
|
||||
column: "AiTokensPerMonth",
|
||||
value: 5000000L);
|
||||
|
||||
migrationBuilder.UpdateData(
|
||||
table: "SubscriptionPlans",
|
||||
keyColumn: "Id",
|
||||
keyValue: "plan-starter",
|
||||
column: "AiTokensPerMonth",
|
||||
value: 0L);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void Down(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.DropColumn(
|
||||
name: "AiTokensPerMonth",
|
||||
table: "SubscriptionPlans");
|
||||
|
||||
migrationBuilder.DropColumn(
|
||||
name: "AiTokensPerMonth",
|
||||
table: "Instances");
|
||||
|
||||
migrationBuilder.DropColumn(
|
||||
name: "AiTokensThisMonth",
|
||||
table: "Instances");
|
||||
|
||||
migrationBuilder.AddColumn<int>(
|
||||
name: "AiRequestsPerMonth",
|
||||
table: "SubscriptionPlans",
|
||||
type: "integer",
|
||||
nullable: false,
|
||||
defaultValue: 0);
|
||||
|
||||
migrationBuilder.AddColumn<int>(
|
||||
name: "AiRequestsPerMonth",
|
||||
table: "Instances",
|
||||
type: "integer",
|
||||
nullable: false,
|
||||
defaultValue: 0);
|
||||
|
||||
migrationBuilder.AddColumn<int>(
|
||||
name: "AiRequestsThisMonth",
|
||||
table: "Instances",
|
||||
type: "integer",
|
||||
nullable: false,
|
||||
defaultValue: 0);
|
||||
|
||||
migrationBuilder.UpdateData(
|
||||
table: "SubscriptionPlans",
|
||||
keyColumn: "Id",
|
||||
keyValue: "plan-premium",
|
||||
column: "AiRequestsPerMonth",
|
||||
value: 2000);
|
||||
|
||||
migrationBuilder.UpdateData(
|
||||
table: "SubscriptionPlans",
|
||||
keyColumn: "Id",
|
||||
keyValue: "plan-standard",
|
||||
column: "AiRequestsPerMonth",
|
||||
value: 500);
|
||||
|
||||
migrationBuilder.UpdateData(
|
||||
table: "SubscriptionPlans",
|
||||
keyColumn: "Id",
|
||||
keyValue: "plan-starter",
|
||||
column: "AiRequestsPerMonth",
|
||||
value: 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -41,6 +41,9 @@ namespace ManagerService.Migrations
|
||||
b.Property<DateTime?>("DateExpiration")
|
||||
.HasColumnType("timestamp with time zone");
|
||||
|
||||
b.Property<DateTime>("DateUpdate")
|
||||
.HasColumnType("timestamp with time zone");
|
||||
|
||||
b.Property<string>("InstanceId")
|
||||
.IsRequired()
|
||||
.HasColumnType("text");
|
||||
@ -78,6 +81,12 @@ namespace ManagerService.Migrations
|
||||
.IsRequired()
|
||||
.HasColumnType("text");
|
||||
|
||||
b.Property<DateTime>("DateCreation")
|
||||
.HasColumnType("timestamp with time zone");
|
||||
|
||||
b.Property<DateTime>("DateUpdate")
|
||||
.HasColumnType("timestamp with time zone");
|
||||
|
||||
b.Property<string>("DeviceId")
|
||||
.HasColumnType("text");
|
||||
|
||||
@ -139,6 +148,12 @@ namespace ManagerService.Migrations
|
||||
b.Property<int>("AppType")
|
||||
.HasColumnType("integer");
|
||||
|
||||
b.Property<DateTime>("DateCreation")
|
||||
.HasColumnType("timestamp with time zone");
|
||||
|
||||
b.Property<DateTime>("DateUpdate")
|
||||
.HasColumnType("timestamp with time zone");
|
||||
|
||||
b.Property<string>("InstanceId")
|
||||
.IsRequired()
|
||||
.HasColumnType("text");
|
||||
@ -172,6 +187,8 @@ namespace ManagerService.Migrations
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("InstanceId");
|
||||
|
||||
b.HasIndex("SectionEventId");
|
||||
|
||||
b.ToTable("ApplicationInstances");
|
||||
@ -208,6 +225,8 @@ namespace ManagerService.Migrations
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("InstanceId");
|
||||
|
||||
b.ToTable("AuditLogs");
|
||||
});
|
||||
|
||||
@ -219,6 +238,9 @@ namespace ManagerService.Migrations
|
||||
b.Property<DateTime>("DateCreation")
|
||||
.HasColumnType("timestamp with time zone");
|
||||
|
||||
b.Property<DateTime>("DateUpdate")
|
||||
.HasColumnType("timestamp with time zone");
|
||||
|
||||
b.Property<string>("ImageId")
|
||||
.HasColumnType("text");
|
||||
|
||||
@ -266,6 +288,8 @@ namespace ManagerService.Migrations
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("InstanceId");
|
||||
|
||||
b.ToTable("Configurations");
|
||||
});
|
||||
|
||||
@ -325,6 +349,8 @@ namespace ManagerService.Migrations
|
||||
|
||||
b.HasIndex("ConfigurationId");
|
||||
|
||||
b.HasIndex("InstanceId");
|
||||
|
||||
b.ToTable("Devices");
|
||||
});
|
||||
|
||||
@ -333,11 +359,11 @@ namespace ManagerService.Migrations
|
||||
b.Property<string>("Id")
|
||||
.HasColumnType("text");
|
||||
|
||||
b.Property<int>("AiRequestsPerMonth")
|
||||
.HasColumnType("integer");
|
||||
b.Property<long>("AiTokensPerMonth")
|
||||
.HasColumnType("bigint");
|
||||
|
||||
b.Property<int>("AiRequestsThisMonth")
|
||||
.HasColumnType("integer");
|
||||
b.Property<long>("AiTokensThisMonth")
|
||||
.HasColumnType("bigint");
|
||||
|
||||
b.Property<string>("AiUsageMonthKey")
|
||||
.HasColumnType("text");
|
||||
@ -345,6 +371,9 @@ namespace ManagerService.Migrations
|
||||
b.Property<DateTime>("DateCreation")
|
||||
.HasColumnType("timestamp with time zone");
|
||||
|
||||
b.Property<DateTime>("DateUpdate")
|
||||
.HasColumnType("timestamp with time zone");
|
||||
|
||||
b.Property<bool>("HasAdvancedStats")
|
||||
.HasColumnType("boolean");
|
||||
|
||||
@ -449,6 +478,9 @@ namespace ManagerService.Migrations
|
||||
b.Property<DateTime>("DateCreation")
|
||||
.HasColumnType("timestamp with time zone");
|
||||
|
||||
b.Property<DateTime>("DateUpdate")
|
||||
.HasColumnType("timestamp with time zone");
|
||||
|
||||
b.Property<string>("InstanceId")
|
||||
.IsRequired()
|
||||
.HasColumnType("text");
|
||||
@ -468,6 +500,8 @@ namespace ManagerService.Migrations
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("InstanceId");
|
||||
|
||||
b.ToTable("Resources");
|
||||
});
|
||||
|
||||
@ -486,6 +520,9 @@ namespace ManagerService.Migrations
|
||||
b.Property<DateTime>("DateCreation")
|
||||
.HasColumnType("timestamp with time zone");
|
||||
|
||||
b.Property<DateTime>("DateUpdate")
|
||||
.HasColumnType("timestamp with time zone");
|
||||
|
||||
b.Property<string>("Description")
|
||||
.HasColumnType("jsonb");
|
||||
|
||||
@ -544,6 +581,8 @@ namespace ManagerService.Migrations
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("InstanceId");
|
||||
|
||||
b.HasIndex("SectionMenuId");
|
||||
|
||||
b.ToTable("Sections");
|
||||
@ -705,6 +744,12 @@ namespace ManagerService.Migrations
|
||||
b.Property<string>("Id")
|
||||
.HasColumnType("text");
|
||||
|
||||
b.Property<DateTime>("DateCreation")
|
||||
.HasColumnType("timestamp with time zone");
|
||||
|
||||
b.Property<DateTime>("DateUpdate")
|
||||
.HasColumnType("timestamp with time zone");
|
||||
|
||||
b.Property<string>("Description")
|
||||
.HasColumnType("jsonb");
|
||||
|
||||
@ -753,6 +798,8 @@ namespace ManagerService.Migrations
|
||||
|
||||
b.HasIndex("ImageResourceId");
|
||||
|
||||
b.HasIndex("InstanceId");
|
||||
|
||||
b.HasIndex("SectionEventId");
|
||||
|
||||
b.HasIndex("SectionParcoursId");
|
||||
@ -953,8 +1000,8 @@ namespace ManagerService.Migrations
|
||||
b.Property<string>("Id")
|
||||
.HasColumnType("text");
|
||||
|
||||
b.Property<int>("AiRequestsPerMonth")
|
||||
.HasColumnType("integer");
|
||||
b.Property<long>("AiTokensPerMonth")
|
||||
.HasColumnType("bigint");
|
||||
|
||||
b.Property<bool>("HasAdvancedStats")
|
||||
.HasColumnType("boolean");
|
||||
@ -980,7 +1027,7 @@ namespace ManagerService.Migrations
|
||||
new
|
||||
{
|
||||
Id = "plan-starter",
|
||||
AiRequestsPerMonth = 0,
|
||||
AiTokensPerMonth = 0L,
|
||||
HasAdvancedStats = false,
|
||||
HasStats = false,
|
||||
Name = "Starter",
|
||||
@ -990,7 +1037,7 @@ namespace ManagerService.Migrations
|
||||
new
|
||||
{
|
||||
Id = "plan-standard",
|
||||
AiRequestsPerMonth = 500,
|
||||
AiTokensPerMonth = 5000000L,
|
||||
HasAdvancedStats = false,
|
||||
HasStats = true,
|
||||
Name = "Standard",
|
||||
@ -1000,7 +1047,7 @@ namespace ManagerService.Migrations
|
||||
new
|
||||
{
|
||||
Id = "plan-premium",
|
||||
AiRequestsPerMonth = 2000,
|
||||
AiTokensPerMonth = 20000000L,
|
||||
HasAdvancedStats = true,
|
||||
HasStats = true,
|
||||
Name = "Premium",
|
||||
@ -1017,6 +1064,9 @@ namespace ManagerService.Migrations
|
||||
b.Property<DateTime>("DateCreation")
|
||||
.HasColumnType("timestamp with time zone");
|
||||
|
||||
b.Property<DateTime>("DateUpdate")
|
||||
.HasColumnType("timestamp with time zone");
|
||||
|
||||
b.Property<string>("Email")
|
||||
.IsRequired()
|
||||
.HasColumnType("text");
|
||||
@ -1045,6 +1095,8 @@ namespace ManagerService.Migrations
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("InstanceId");
|
||||
|
||||
b.ToTable("Users");
|
||||
});
|
||||
|
||||
|
||||
@ -57,7 +57,7 @@ namespace ManagerService.Services
|
||||
}
|
||||
|
||||
var translations = Newtonsoft.Json.JsonConvert.DeserializeObject<Dictionary<string, string>>(json) ?? new();
|
||||
return new AiTranslateResponse { Translations = translations };
|
||||
return new AiTranslateResponse { Translations = translations, TokensUsed = response.Usage?.TotalTokenCount ?? 0 };
|
||||
}
|
||||
|
||||
private static string StripHtml(string? html) =>
|
||||
@ -493,7 +493,7 @@ namespace ManagerService.Services
|
||||
reply = response.Text ?? "";
|
||||
expectsReply = true;
|
||||
}
|
||||
return new AiChatResponse { Reply = reply, Cards = cards, Navigation = navigation, ExpectsReply = expectsReply };
|
||||
return new AiChatResponse { Reply = reply, Cards = cards, Navigation = navigation, ExpectsReply = expectsReply, TokensUsed = response.Usage?.TotalTokenCount ?? 0 };
|
||||
}
|
||||
catch (System.ClientModel.ClientResultException ex)
|
||||
{
|
||||
@ -789,7 +789,7 @@ namespace ManagerService.Services
|
||||
reply = response.Text ?? "";
|
||||
expectsReply = true;
|
||||
}
|
||||
return new AiChatResponse { Reply = reply, Navigation = navigation, ExpectsReply = expectsReply };
|
||||
return new AiChatResponse { Reply = reply, Navigation = navigation, ExpectsReply = expectsReply, TokensUsed = response.Usage?.TotalTokenCount ?? 0 };
|
||||
}
|
||||
catch (System.ClientModel.ClientResultException ex)
|
||||
{
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user