diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 472b9fe..5cb6f08 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -1,5 +1,5 @@ variables: - VERS_MAIN: '1.0' + VERS_MAIN: '1.2' NEXUS_PATH: 'LiMan' APP_NAME: 'LiMan.UI' SOL_NAME: 'LiMan.UI' diff --git a/Core/Const.cs b/Core/Const.cs index 37fbd8d..51aec76 100644 --- a/Core/Const.cs +++ b/Core/Const.cs @@ -12,5 +12,6 @@ namespace Core // classi utilità x cache REDIS dati DB public const string redisBaseAddr = "LiMan:Ui"; public const string rKeyConfig = $"{redisBaseAddr}:Cache"; + public const string ENR_MSG_PIPE = $"{redisBaseAddr}:Enroll:Messages"; } } diff --git a/LiMan.Api/Controllers/EnrollerController.cs b/LiMan.Api/Controllers/EnrollerController.cs new file mode 100644 index 0000000..45d2368 --- /dev/null +++ b/LiMan.Api/Controllers/EnrollerController.cs @@ -0,0 +1,137 @@ +using Core; +using LiMan.APi.Data; +using LiMan.DB.DBModels; +using Microsoft.AspNetCore.Http; +using Microsoft.AspNetCore.Mvc; +using Newtonsoft.Json; +using NLog; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; + +namespace LiMan.APi.Controllers +{ + /// + /// Controller livello APPLICAZIONE + /// + [Route("api/enroller")] + [ApiController] + public class EnrollerController : ControllerBase + { + #region Public Constructors + + /// + /// Init generico + /// + /// + public EnrollerController(ApiDataService DataService) + { + dataService = DataService; + Log.Info("Avviata classe ApplicazioneController"); + } + + #endregion Public Constructors + + #region Public Methods + + /// + /// Recupera record di enroll di una richiesta x ricavarne ID licenza da applicare + /// + /// ID richiesta + /// passcode associato + /// + [HttpGet("{id}")] + public async Task Get(string id, int passcode) + { + string CodInst = "NA"; + string CodApp = "Updater"; + int reqId = 0; + int.TryParse(id, out reqId); + EnrollRequestModel reqRec = await dataService.EnrollReqGetById(reqId); + // solo se il passcode è corretto restituisco record, altrimenti fake one... + if (reqRec != null && reqRec.Passcode != passcode) + { + reqRec = new EnrollRequestModel() { IdReq = reqId }; + } + await dataService.recordCall(CodInst, CodApp, $"GET:api/enroller/{id}"); + return reqRec; + } + + /// + /// Recupera record della licenza (mainKey) associata ad una richiesta date le sue info ID, passcode, ID licenza asegnata + /// + /// ID richiesta + /// passcode associato + /// ID licenza associato + /// + [HttpGet("getLicense/{id}")] + public async Task GetLicData(string id, int passcode, int idLic) + { + string CodInst = "NA"; + string CodApp = "Updater"; + int reqId = 0; + int.TryParse(id, out reqId); + EnrollRequestModel reqRec = await dataService.EnrollReqGetById(reqId); + // init licenza non valida + LicenzaModel licRec = new LicenzaModel() + { + IdxLic = idLic, + Chiave = "", + CodInst = "NA", + CodApp = "None", + NumLicenze = 0, + Scadenza = DateTime.Today.AddYears(-1), + Payload = "", + Enigma = "" + }; + // solo se sono corretti passcode e idLic corretto restituisco record, altrimenti fake one... + if (reqRec != null && reqRec.Passcode == passcode && reqRec.IdxLic == idLic) + { + licRec = await dataService.LicenzaById(idLic); + CodInst = licRec.CodInst; + CodApp = licRec.CodApp; + } + await dataService.recordCall(CodInst, CodApp, $"GET:api/enroller/getLicense/{id}"); + return licRec; + } + + /// + /// Richiesta di un record con codice TOTP per l'enroll di un app client + /// + [HttpPost("getNewEnrollRec")] + public async Task GetNewEnrollRec([FromBody] Dictionary MachineInfo) + { + string CodInst = "NA"; + string CodApp = "Updater"; + var newRec = await dataService.EnrollReqCreate(MachineInfo); + await dataService.recordCall(CodInst, CodApp, $"GET:api/enroller/GetEnrollRec"); + return newRec; + } + + #endregion Public Methods + + #region Protected Properties + + /// + /// Dataservice x accesso DB + /// + protected ApiDataService dataService { get; set; } + + #endregion Protected Properties + + #region Private Fields + + /// + /// Classe per logging + /// + private static Logger Log = LogManager.GetCurrentClassLogger(); + + /// + /// Generatore pseudocasuale + /// + private Random rnd = new Random(); + + #endregion Private Fields + } +} \ No newline at end of file diff --git a/LiMan.Api/Data/ApiDataService.cs b/LiMan.Api/Data/ApiDataService.cs index 5bdf151..59a547c 100644 --- a/LiMan.Api/Data/ApiDataService.cs +++ b/LiMan.Api/Data/ApiDataService.cs @@ -1,13 +1,16 @@ using Core; using Core.DTO; +using LiMan.DB; using LiMan.DB.DBModels; using LiMan.DB.DTO; using Microsoft.AspNetCore.Identity.UI.Services; using Microsoft.Extensions.Caching.Distributed; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.Logging; +using Microsoft.VisualBasic; using Newtonsoft.Json; using NLog; +using Org.BouncyCastle.Asn1.X500; using StackExchange.Redis; using StackExchange.Redis.Extensions.Core.Abstractions; using System; @@ -61,6 +64,9 @@ namespace LiMan.APi.Data ReferenceLoopHandling = ReferenceLoopHandling.Ignore }; + // conf messagepipe: setup canali pub/sub + EnrollMessPipe = new MessagePipe(redisConn, Const.ENR_MSG_PIPE); + // conf DB string connStrDB = _configuration.GetConnectionString("LiMan.DB"); if (string.IsNullOrEmpty(connStrDB)) @@ -295,13 +301,13 @@ namespace LiMan.APi.Data { bool taskDone = false; - Stopwatch stopWatch = new Stopwatch(); - stopWatch.Start(); + Stopwatch sw = new Stopwatch(); + sw.Start(); taskDone = dbController.AttivazioniTryRefresh(MasterKey, ParamDict); await InvalidateAllCache(); - stopWatch.Stop(); - TimeSpan ts = stopWatch.Elapsed; + sw.Stop(); + TimeSpan ts = sw.Elapsed; Log.Trace($"Effettuata scrittura + rilettura da DB per AttivazioniTryRefresh: {ts.TotalMilliseconds} ms"); return await Task.FromResult(taskDone); @@ -316,6 +322,207 @@ namespace LiMan.APi.Data dbController.Dispose(); } + /// + /// Crea record richiesta enroll (univoco rispetto richieste correnti) + /// + /// Dati da associare alla richeista + /// + public async Task EnrollReqCreate(Dictionary MachineInfo) + { + Stopwatch sw = new Stopwatch(); + sw.Start(); + // svuoto elenco richieste scaduteù + var cleanDone = await EnrollReqPurgeInvalid(); + // prendo elenco attive x evitare duplicazioni codici TOTP... + var resList = await EnrollReqGetActive(); + int totpCode = rnd.Next(1, 100000000); + // verifico che non sia preesistente... + if (resList.Count > 0) + { + // cerco se fosse già presente... + while (resList.Where(x => x.Passcode == totpCode).Any()) + { + // genero nuovo... + await Task.Delay(rnd.Next(50)); + totpCode = rnd.Next(1, 100000000); + } + } + + // serializzo i dati della richiesta.. + string reqPayload = JsonConvert.SerializeObject(MachineInfo); + + // preparo il record da registrare... + EnrollRequestModel newReq = new EnrollRequestModel() + { + DtReq = DateTime.Now, + Passcode = totpCode, + ReqPayload = reqPayload + }; + + var dbRec = await EnrollReqUpsert(newReq); + sw.Stop(); + TimeSpan ts = sw.Elapsed; + Log.Trace($"Effettuata EnrollReqCreate: {ts.TotalMilliseconds} ms"); + + // invio string in messagepipe x forzare refresh... + EnrollMessPipe.sendMessage("NewEnrollReq"); + + // restituisce record + return dbRec; + } + + /// + /// Elimino una richiesta enroll (anche se già approvata...) + /// + /// ID record + public async Task EnrollReqDelete(int idReq) + { + bool fatto = false; + // inserimento! + Stopwatch sw = new Stopwatch(); + sw.Start(); + fatto = dbController.EnrollReqDelete(idReq); + // svuota eventuale cache redis... + await FlushRedisCachePattern("Enroll"); + sw.Stop(); + TimeSpan ts = sw.Elapsed; + Log.Trace($"Effettuata EnrollReqDelete: {ts.TotalMilliseconds} ms"); + return fatto; + } + + /// + /// Recupera una richiesta dato suo ID x verificare approvazione e dati associati... + /// + /// ID record + public async Task EnrollReqGetById(int idReq) + { + string source = "DB"; + EnrollRequestModel dbResult = new EnrollRequestModel() { IdReq = idReq }; + try + { + string currKey = $"{Const.rKeyConfig}:Enroll:ById:{idReq}"; + Stopwatch sw = new Stopwatch(); + sw.Start(); + string? rawData = await redisDb.StringGetAsync(currKey); + if (!string.IsNullOrEmpty(rawData)) + { + source = "REDIS"; + var tempResult = JsonConvert.DeserializeObject(rawData); + if (tempResult == null) + { + dbResult = new EnrollRequestModel() { IdReq = idReq }; + } + else + { + dbResult = tempResult; + } + } + else + { + dbResult = dbController.EnrollReqGetById(idReq); + rawData = JsonConvert.SerializeObject(dbResult, JSSettings); + await redisDb.StringSetAsync(currKey, rawData, FastCache); + } + if (dbResult == null) + { + dbResult = new EnrollRequestModel() { IdReq = idReq }; + } + sw.Stop(); + TimeSpan ts = sw.Elapsed; + Log.Debug($"EnrollReqGetById | {source} in: {ts.TotalMilliseconds} ms"); + } + catch (Exception exc) + { + Log.Error($"Error during EnrollReqGetById:{Environment.NewLine}{exc}"); + } + return dbResult; + } + + /// + /// Elenco richeiste enroll attive al momento + /// + /// + public async Task> EnrollReqGetActive() + { + string source = "DB"; + List dbResult = new List(); + try + { + string currKey = $"{Const.rKeyConfig}:Enroll:ActiveList"; + Stopwatch sw = new Stopwatch(); + sw.Start(); + string? rawData = await redisDb.StringGetAsync(currKey); + if (!string.IsNullOrEmpty(rawData)) + { + source = "REDIS"; + var tempResult = JsonConvert.DeserializeObject>(rawData); + if (tempResult == null) + { + dbResult = new List(); + } + else + { + dbResult = tempResult; + } + } + else + { + dbResult = dbController.EnrollReqGetActive(); + rawData = JsonConvert.SerializeObject(dbResult, JSSettings); + await redisDb.StringSetAsync(currKey, rawData, LongCache); + } + if (dbResult == null) + { + dbResult = new List(); + } + sw.Stop(); + TimeSpan ts = sw.Elapsed; + Log.Debug($"EnrollReqGetActive | {source} in: {ts.TotalMilliseconds} ms"); + } + catch (Exception exc) + { + Log.Error($"Error during EnrollReqGetActive:{Environment.NewLine}{exc}"); + } + return dbResult; + } + + /// + /// Elimino eventuali richieste non approvate e scadute + /// + public async Task EnrollReqPurgeInvalid() + { + var reqPurged = dbController.EnrollReqPurgeInvalid(); + // invio string in messagepipe x forzare refresh... + EnrollMessPipe.sendMessage("NewEnrollReq"); + // svuota eventuale cache redis... + await FlushRedisCachePattern("Enroll"); + return reqPurged; + } + + /// + /// Upsert record richiesta enroll + /// + /// + /// + public async Task EnrollReqUpsert(EnrollRequestModel newRec) + { + int recId = 0; + // inserimento! + Stopwatch sw = new Stopwatch(); + sw.Start(); + recId = dbController.EnrollReqUpsert(newRec); + await FlushRedisCachePattern("Enroll"); + var dbResult = await EnrollReqGetById(recId); + // svuota eventuale cache redis... + sw.Stop(); + TimeSpan ts = sw.Elapsed; + Log.Trace($"Effettuata EnrollReqUpsert: {ts.TotalMilliseconds} ms"); + // invio string in messagepipe x forzare refresh... + EnrollMessPipe.sendMessage("NewEnrollReq"); + // restituisce risultato + return dbResult; + } + /// /// Esegue aggiunta file dato ticket e list uploadResult /// @@ -327,11 +534,11 @@ namespace LiMan.APi.Data { bool fatto = false; // inserimento! - Stopwatch stopWatch = new Stopwatch(); - stopWatch.Start(); + Stopwatch sw = new Stopwatch(); + sw.Start(); fatto = dbController.FileAdd(idxTicket, baseDir, fileUploaded); - stopWatch.Stop(); - TimeSpan ts = stopWatch.Elapsed; + sw.Stop(); + TimeSpan ts = sw.Elapsed; Log.Trace($"Effettuata inserimento con FileAdd: {ts.TotalMilliseconds} ms"); // restituisce elenco @@ -356,7 +563,12 @@ namespace LiMan.APi.Data } /// - /// Refresh globale cache redis + /// Wrapper x invio/ricezione messaggi sul canale dedicato agli eventi enroll + /// + public MessagePipe EnrollMessPipe { get; set; } = null!; + + /// + /// Refresh globale cache Redis /// /// public async Task FlushRedisCache() @@ -371,6 +583,23 @@ namespace LiMan.APi.Data return answ; } + /// + /// Refresh cache Redis dato redPattern + /// + /// Pattern da eliminare + /// + public async Task FlushRedisCachePattern(string pattern) + { + Stopwatch sw = new Stopwatch(); + sw.Start(); + await Task.Delay(1); + RedisValue redPattern = new RedisValue($"{Const.rKeyConfig}:{pattern}*"); + bool answ = await ExecFlushRedisPattern(redPattern); + sw.Stop(); + Log.Debug($"FlushRedisCachePattern in {sw.Elapsed.TotalMilliseconds} ms"); + return answ; + } + /// /// invalida tutta la cache in caso di update /// @@ -385,7 +614,7 @@ namespace LiMan.APi.Data } /// - /// Elenco licenze dato cliente + /// Record licenza data masterKey /// /// Chiave Licenza x ricerca /// @@ -417,6 +646,39 @@ namespace LiMan.APi.Data return await Task.FromResult(dbResult); } + /// + /// Elenco licenze dato ID + /// + /// ID licenza (DB) + /// + public async Task LicenzaById(int licId) + { + LicenzaModel dbResult = new LicenzaModel(); + string cacheKey = $"{rKeyLicById}:{licId}"; + trackCache(cacheKey); + string rawData = await getRSV(cacheKey); + if (!string.IsNullOrEmpty(rawData)) + { + dbResult = JsonConvert.DeserializeObject(rawData); + } + else + { + Stopwatch stopWatch = new Stopwatch(); + stopWatch.Start(); + dbResult = dbController.LicenzaById(licId); + if (dbResult != null) + { + rawData = JsonConvert.SerializeObject(dbResult); + await setRSV(cacheKey, rawData, hourTTL); + } + stopWatch.Stop(); + TimeSpan ts = stopWatch.Elapsed; + Log.Trace($"Effettuata lettura da DB per LicenzaById: {ts.TotalMilliseconds} ms"); + } + + return dbResult; + } + /// /// Effettua refresh del payload della licenza dato info + enigma generato dal client /// @@ -827,6 +1089,11 @@ namespace LiMan.APi.Data /// protected const string rKeyAttivByLic = "LiMan.UI:Licenze:AttByIdxLic"; + /// + /// Chiave redis x licenze da ID + /// + protected const string rKeyLicById = "LiMan.UI:Licenze:ById"; + /// /// Chiave redis x licenze da MasterKey /// @@ -849,6 +1116,16 @@ namespace LiMan.APi.Data protected static JsonSerializerSettings? JSSettings; + /// + /// Durata cache lunga IN SECONDI + /// + protected int cacheTtlLong = 60 * 5; + + /// + /// Durata cache breve IN SECONDI + /// + protected int cacheTtlShort = 60 * 1; + /// /// Oggetto per connessione a REDIS /// @@ -859,12 +1136,50 @@ namespace LiMan.APi.Data /// protected IDatabase redisDb = null!; + protected Random rnd = new Random(); + #endregion Protected Fields + #region Protected Properties + + /// + /// Durata cache breve (1 min circa + perturbazione percentuale +/-10%) + /// + protected TimeSpan FastCache + { + get => TimeSpan.FromSeconds(cacheTtlShort * rnd.Next(900, 1100) / 1000); + } + + /// + /// Durata cache lunga (+ perturbazione percentuale +/-10%) + /// + protected TimeSpan LongCache + { + get => TimeSpan.FromSeconds(cacheTtlLong * rnd.Next(900, 1100) / 1000); + } + + /// + /// Durata cache MOLTO breve (10 sec circa + perturbazione percentuale +/-10%) + /// + protected TimeSpan UltraFastCache + { + get => TimeSpan.FromSeconds(cacheTtlShort / 6 * rnd.Next(900, 1100) / 1000); + } + + /// + /// Durata cache MOLTO lunga (+ perturbazione percentuale +/-10%) + /// + protected TimeSpan UltraLongCache + { + get => TimeSpan.FromSeconds(cacheTtlLong * 10 * rnd.Next(900, 1100) / 1000); + } + + #endregion Protected Properties + #region Protected Methods /// - /// Esegue flush memoria redis dato pattern + /// Esegue flush memoria redis dato redPattern /// /// /// diff --git a/LiMan.Api/LiMan.APi.csproj b/LiMan.Api/LiMan.APi.csproj index 1a9522a..546dc17 100644 --- a/LiMan.Api/LiMan.APi.csproj +++ b/LiMan.Api/LiMan.APi.csproj @@ -22,8 +22,8 @@ - + diff --git a/LiMan.Api/Resources/ChangeLog.html b/LiMan.Api/Resources/ChangeLog.html index e9b807e..7c4ac29 100644 --- a/LiMan.Api/Resources/ChangeLog.html +++ b/LiMan.Api/Resources/ChangeLog.html @@ -1,6 +1,6 @@ License Manager -

Versione: 1.1.2410.1815

+

Versione: 1.1.2501.0411


Note di rilascio:
  • diff --git a/LiMan.Api/Resources/VersNum.txt b/LiMan.Api/Resources/VersNum.txt index 071a1f9..19632ec 100644 --- a/LiMan.Api/Resources/VersNum.txt +++ b/LiMan.Api/Resources/VersNum.txt @@ -1 +1 @@ -1.1.2410.1815 +1.1.2501.0411 diff --git a/LiMan.Api/Resources/manifest.xml b/LiMan.Api/Resources/manifest.xml index e8de82d..4e6a2ec 100644 --- a/LiMan.Api/Resources/manifest.xml +++ b/LiMan.Api/Resources/manifest.xml @@ -1,6 +1,6 @@ - 1.1.2410.1815 + 1.1.2501.0411 https://nexus.steamware.net/repository/SWS/LiMan/stable/LAST/LiMan.UI.zip https://nexus.steamware.net/repository/SWS/LiMan/stable/LAST/ChangeLog.html false diff --git a/LiMan.Api/appsettings.json b/LiMan.Api/appsettings.json index f097533..9f9aeed 100644 --- a/LiMan.Api/appsettings.json +++ b/LiMan.Api/appsettings.json @@ -44,7 +44,7 @@ "Database": 14 }, "ServerConf": { - "FileShareAppBackup": "\\\\stor01\\TEAM DRIVES\\40_FileUpload\\AppBackup", + "FileShareAppBackup": "\\\\stor01\\AppBackup", "FileShareTickets": "\\\\stor01\\TEAM DRIVES\\40_FileUpload\\unsafe_uploads" }, "ApiUrl": "https://liman.egalware.com/", diff --git a/LiMan.DB/Controllers/DbController.cs b/LiMan.DB/Controllers/DbController.cs index 7d90383..d65692c 100644 --- a/LiMan.DB/Controllers/DbController.cs +++ b/LiMan.DB/Controllers/DbController.cs @@ -608,7 +608,6 @@ namespace LiMan.DB.Controllers List dbResult = new List(); using (LMDbContext localDbCtx = new LMDbContext(_configuration)) { - DateTime oggi = DateTime.Today; dbResult = localDbCtx .DbSetRoles .ToList(); @@ -740,6 +739,181 @@ namespace LiMan.DB.Controllers //Log.Info("Dispose di GWMSController"); } + /// + /// Elimino una richiesta enroll (anche se già approvata...) + /// + /// licId record da eliminare + public bool EnrollReqDelete(int idReq) + { + bool fatto = false; + using (LMDbContext localDbCtx = new LMDbContext(_configuration)) + { + // se trovo... procedo... + var rec2del = localDbCtx + .DbSetEnrollReq + .Where(x => x.IdReq == idReq) + .FirstOrDefault(); + if (rec2del != null) + { + localDbCtx + .DbSetEnrollReq + .Remove(rec2del); + // salvo + localDbCtx.SaveChanges(); + fatto = true; + } + } + return fatto; + } + + /// + /// Elenco Enroll Attivi (non completati/cancellati) + /// + /// + public List EnrollReqGetActive() + { + List dbResult = new List(); + using (LMDbContext localDbCtx = new LMDbContext(_configuration)) + { + dbResult = localDbCtx + .DbSetEnrollReq + .Where(x => x.DtAppr == null) + .OrderByDescending(x => x.DtReq) + .ToList(); + } + return dbResult; + } + + /// + /// Elenco completo Enroll Req + /// + /// + public List EnrollReqGetAll() + { + List dbResult = new List(); + using (LMDbContext localDbCtx = new LMDbContext(_configuration)) + { + dbResult = localDbCtx + .DbSetEnrollReq + .OrderByDescending(x => x.DtReq) + .ToList(); + } + return dbResult; + } + + /// + /// Recupero richiesta da licId (oppure empty...) + /// + /// + /// + public EnrollRequestModel EnrollReqGetById(int idReq) + { + EnrollRequestModel dbResult = new EnrollRequestModel(); + using (LMDbContext localDbCtx = new LMDbContext(_configuration)) + { + dbResult = localDbCtx + .DbSetEnrollReq + .FirstOrDefault(x => x.IdReq == idReq); + } + return dbResult; + } + + /// + /// Elenco filtrato Enroll Req x data + /// + /// + /// + /// + public List EnrollReqGetFilt(DateTime dtFrom, DateTime dtTo) + { + List dbResult = new List(); + using (LMDbContext localDbCtx = new LMDbContext(_configuration)) + { + dbResult = localDbCtx + .DbSetEnrollReq + .Where(x => x.DtReq >= dtFrom && x.DtReq <= dtTo) + .OrderByDescending(x => x.DtReq) + .ToList(); + } + return dbResult; + } + + /// + /// Elimino eventuali richieste non approvate e scadute + /// + public bool EnrollReqPurgeInvalid() + { + bool fatto = false; + using (LMDbContext localDbCtx = new LMDbContext(_configuration)) + { + List item2del = new List(); + // cerco in primis richieste NON associate a licenza + var list2check = localDbCtx + .DbSetEnrollReq + .Where(x => x.IdxLic == 0) + .ToList(); + var list2del = list2check + .Where(x => x.IsScaduta) + .ToList(); + if (list2del != null) + { + localDbCtx + .DbSetEnrollReq + .RemoveRange(list2del); + // salvo + int numDone = localDbCtx.SaveChanges(); + fatto = numDone != 0; + } + } + return fatto; + } + + /// + /// Upsert record richiesta enroll + /// + /// + /// + public int EnrollReqUpsert(EnrollRequestModel newRec) + { + int answ = 0; + using (LMDbContext localDbCtx = new LMDbContext(_configuration)) + { + try + { + var currData = localDbCtx + .DbSetEnrollReq + .Where(x => x.IdReq == newRec.IdReq && newRec.IdReq > 0) + .FirstOrDefault(); + if (currData != null) + { + currData.Passcode = newRec.Passcode; + currData.ReqPayload = newRec.ReqPayload; + currData.DtReq = newRec.DtReq; + currData.DtAppr = newRec.DtAppr; + currData.UserAppr = newRec.UserAppr; + currData.IdxLic = newRec.IdxLic; + // segno aggiornato + localDbCtx.Entry(currData).State = EntityState.Modified; + localDbCtx.SaveChanges(); + answ = newRec.IdReq; + } + else + { + var result = localDbCtx + .DbSetEnrollReq + .Add(newRec); + localDbCtx.SaveChanges(); + answ = newRec.IdReq; + } + } + catch (Exception exc) + { + Log.Error($"Errore in EnrollReqUpsert | Passcode: {newRec.Passcode} | DtReq: {newRec.DtReq}{Environment.NewLine}{exc}"); + } + } + return answ; + } + /// /// Elenco files attach da registrare /// @@ -1293,6 +1467,29 @@ namespace LiMan.DB.Controllers return Task.FromResult(fatto); } + /// + /// Record licenza da ID + /// + /// + /// + public LicenzaModel LicenzaById(int licId) + { + LicenzaModel dbResult = new LicenzaModel(); + using (LMDbContext localDbCtx = new LMDbContext(_configuration)) + { + DateTime oggi = DateTime.Today; + dbResult = localDbCtx + .DbSetLicenze + .FirstOrDefault(x => x.IdxLic == licId); + } + return dbResult; + } + + /// + /// Record licenza da MasterKey + /// + /// + /// public LicenzaModel LicenzaByKey(string MasterKey) { LicenzaModel dbResult = new LicenzaModel(); @@ -1301,8 +1498,7 @@ namespace LiMan.DB.Controllers DateTime oggi = DateTime.Today; dbResult = localDbCtx .DbSetLicenze - .Where(x => x.Chiave == MasterKey) - .FirstOrDefault(); + .FirstOrDefault(x => x.Chiave == MasterKey); } return dbResult; } diff --git a/LiMan.DB/DBModels/EnrollRequestModel.cs b/LiMan.DB/DBModels/EnrollRequestModel.cs new file mode 100644 index 0000000..3892daf --- /dev/null +++ b/LiMan.DB/DBModels/EnrollRequestModel.cs @@ -0,0 +1,93 @@ +using Newtonsoft.Json; +using System; +using System.Collections.Generic; +using System.ComponentModel.DataAnnotations; +using System.ComponentModel.DataAnnotations.Schema; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +// +// This is here so CodeMaid doesn't reorganize this document +// +namespace LiMan.DB.DBModels +{ + [Table("EnrollRequest")] + public partial class EnrollRequestModel + { + /// + /// ID univoco + /// + [Key, DatabaseGenerated(DatabaseGeneratedOption.Identity)] + public int IdReq { get; set; } + + /// + /// Passcode usato epr autorizzare (rnd al momento della richiesta) + /// + public int Passcode { get; set; } = 0; + + /// + /// Payload richiesta, ovvero la serializzazione json di un Dict[string,string] delle info ricevute + /// + public string ReqPayload { get; set; } = ""; + + /// + /// DataOra richiesta enroll + /// + public DateTime DtReq { get; set; } = DateTime.Now; + + /// + /// DataOra approvazione + /// + public DateTime? DtAppr { get; set; } = null; + + /// + /// Username approvatore + /// + public string UserAppr { get; set; } = ""; + + /// + /// Licenza fornita in risposta alla richiesta + /// + public int IdxLic { get; set; } = 0; + + /// + /// Indica Scaduta se non approvata e richiesta da oltre 15 minuti + /// + [NotMapped] + public bool IsScaduta + { + get => DtAppr == null && DateTime.Now.Subtract(DtReq).TotalMinutes > 15; + } + [NotMapped] + public Dictionary DictAttrib + { + get + { + Dictionary answ = new Dictionary(); + if (!string.IsNullOrEmpty(ReqPayload)) + { + try + { + answ = JsonConvert.DeserializeObject>(ReqPayload); + } + catch { } + } + return answ; + } + } + public int DictNumKVP() + { + return DictAttrib.Count; + } + public Dictionary DictAttribShort(int numMax) + { + Dictionary answ = DictAttrib; + if (answ.Count > numMax) + { + answ = answ.Take(numMax).ToDictionary(x => x.Key, x => x.Value); + } + return answ; + } + } +} diff --git a/LiMan.DB/LMDbContext.cs b/LiMan.DB/LMDbContext.cs index b33a842..712f0d2 100644 --- a/LiMan.DB/LMDbContext.cs +++ b/LiMan.DB/LMDbContext.cs @@ -61,6 +61,7 @@ namespace LiMan.DB public virtual DbSet DbSetRoles { get; set; } public virtual DbSet DbSetClaims { get; set; } public virtual DbSet DbSetReleases { get; set; } + public virtual DbSet DbSetEnrollReq { get; set; } #endregion Public Properties diff --git a/LiMan.DB/MessagePipe.cs b/LiMan.DB/MessagePipe.cs new file mode 100644 index 0000000..46ccea1 --- /dev/null +++ b/LiMan.DB/MessagePipe.cs @@ -0,0 +1,157 @@ +using NLog; +using StackExchange.Redis; +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace LiMan.DB +{ + public class MessagePipe + { + #region Public Constructors + + public MessagePipe(IConnectionMultiplexer redisConn, string channelName, bool enableLog = false) + { + _channel = channelName; + redis = redisConn; + redisDb = redis.GetDatabase(); + this.enableLog = enableLog; + // aggiungo sottoscrittore + setupSubscriber(); + } + + #endregion Public Constructors + + #region Public Events + + public event EventHandler EA_NewMessage = delegate { }; + + #endregion Public Events + + #region Public Methods + + /// + /// Invio messaggio sul canale + salvataggio in cache REDIS + /// + /// Chiave REDIS x salvare valore + /// Messaggio serializzato da inviare + public bool saveAndSendMessage(string memKey, string message) + { + bool answ = false; + Stopwatch stopWatch = new Stopwatch(); + stopWatch.Start(); + + // invio notifica tramite il canale richiesto + answ = sendMessage(message); + if (redisDb != null) + { + redisDb.StringSetAsync(memKey, message); + } + stopWatch.Stop(); + TimeSpan ts = stopWatch.Elapsed; + if (numSent.ContainsKey(memKey)) + { + numSent[memKey]++; + } + else + { + numSent.Add(memKey, 1); + } + if (enableLog || numSent[memKey] > 30) + { + Log.Info($"saveAndSendMessage| mKey {memKey} x {numSent[memKey]} | {message.Length} size | {ts.TotalMilliseconds} ms"); + + numSent[memKey] = 0; + } + return answ; + } + + /// + /// Invio messaggio sul canale + /// + /// + /// + public bool sendMessage(string newMess) + { + bool answ = false; + ISubscriber sub = redis.GetSubscriber(); + sub.Publish(_channel, newMess); + return answ; + } + + #endregion Public Methods + + #region Protected Fields + + protected static Logger Log = LogManager.GetCurrentClassLogger(); + + #endregion Protected Fields + + #region Private Fields + + private bool enableLog = false; + private Dictionary numSent = new Dictionary(); + private IConnectionMultiplexer redis; + private IDatabase redisDb; + + #endregion Private Fields + + #region Private Properties + + /// + /// Canale associato al gestore pipeline messaggi + /// + private string _channel { get; set; } = ""; + + #endregion Private Properties + + #region Private Methods + + private void setupSubscriber() + { + ISubscriber sub = redis.GetSubscriber(); + //Subscribe to the channel named messages + sub.Subscribe(_channel, (channel, message) => + { + if (enableLog) + { + Log.Trace($"req setup ch {channel} | {message}"); + } + // messaggio + PubSubEventArgs mea = new PubSubEventArgs(message); + // se qualcuno ascolta sollevo evento nuovo valore... + if (EA_NewMessage != null) + { + EA_NewMessage(this, mea); + } + }); + if (enableLog) + { + Log.Info($"Subscribed {_channel}"); + } + } + + #endregion Private Methods + } + + public class PubSubEventArgs : EventArgs + { + #region Public Constructors + + public PubSubEventArgs(string messaggio) + { + this.newMessage = messaggio; + } + + #endregion Public Constructors + + #region Public Properties + + public string newMessage { get; set; } = ""; + + #endregion Public Properties + } +} \ No newline at end of file diff --git a/LiMan.DB/Migrations/20241231105435_AddEnrollRequest.Designer.cs b/LiMan.DB/Migrations/20241231105435_AddEnrollRequest.Designer.cs new file mode 100644 index 0000000..23f79f0 --- /dev/null +++ b/LiMan.DB/Migrations/20241231105435_AddEnrollRequest.Designer.cs @@ -0,0 +1,583 @@ +// +using System; +using LiMan.DB; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Metadata; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; + +#nullable disable + +namespace LiMan.DB.Migrations +{ + [DbContext(typeof(LMDbContext))] + [Migration("20241231105435_AddEnrollRequest")] + partial class AddEnrollRequest + { + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .UseCollation("SQL_Latin1_General_CP1_CI_AS") + .HasAnnotation("ProductVersion", "6.0.28") + .HasAnnotation("Relational:MaxIdentifierLength", 128); + + SqlServerModelBuilderExtensions.UseIdentityColumns(modelBuilder, 1L, 1); + + modelBuilder.Entity("LiMan.DB.DBModels.ApplicativoModel", b => + { + b.Property("CodApp") + .HasMaxLength(50) + .HasColumnType("nvarchar(50)"); + + b.Property("Descrizione") + .HasMaxLength(250) + .HasColumnType("nvarchar(250)"); + + b.Property("Tipo") + .HasColumnType("nvarchar(max)"); + + b.Property("TplConnString") + .HasMaxLength(2500) + .HasColumnType("nvarchar(2500)"); + + b.HasKey("CodApp"); + + b.ToTable("Applicativi"); + }); + + modelBuilder.Entity("LiMan.DB.DBModels.AuthClaimModel", b => + { + b.Property("ClaimID") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("ClaimID"), 1L, 1); + + b.Property("DtIns") + .HasColumnType("datetime2"); + + b.Property("DtMod") + .HasColumnType("datetime2"); + + b.Property("RoleID") + .HasColumnType("int"); + + b.Property("UserID") + .HasColumnType("int"); + + b.HasKey("ClaimID"); + + b.HasIndex("RoleID"); + + b.HasIndex("UserID"); + + b.ToTable("AuthClaims"); + }); + + modelBuilder.Entity("LiMan.DB.DBModels.AuthRoleModel", b => + { + b.Property("RoleID") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("RoleID"), 1L, 1); + + b.Property("Descrizione") + .HasColumnType("nvarchar(max)"); + + b.Property("Ruolo") + .HasColumnType("nvarchar(max)"); + + b.HasKey("RoleID"); + + b.ToTable("AuthRoles"); + }); + + modelBuilder.Entity("LiMan.DB.DBModels.AuthUserModel", b => + { + b.Property("UserID") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("UserID"), 1L, 1); + + b.Property("AD_Domain") + .HasColumnType("nvarchar(max)"); + + b.Property("AD_User") + .HasColumnType("nvarchar(max)"); + + b.Property("Cognome") + .HasColumnType("nvarchar(max)"); + + b.Property("Nome") + .HasColumnType("nvarchar(max)"); + + b.Property("Username") + .HasColumnType("nvarchar(max)"); + + b.HasKey("UserID"); + + b.ToTable("AuthUsers"); + }); + + modelBuilder.Entity("LiMan.DB.DBModels.EnrollRequestModel", b => + { + b.Property("IdReq") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("IdReq"), 1L, 1); + + b.Property("DtAppr") + .HasColumnType("datetime2"); + + b.Property("DtReq") + .HasColumnType("datetime2"); + + b.Property("IdxLic") + .HasColumnType("int"); + + b.Property("Passcode") + .HasColumnType("int"); + + b.Property("ReqPayload") + .HasColumnType("nvarchar(max)"); + + b.Property("UserAppr") + .HasColumnType("nvarchar(max)"); + + b.HasKey("IdReq"); + + b.ToTable("EnrollRequest"); + }); + + modelBuilder.Entity("LiMan.DB.DBModels.FileAttachModel", b => + { + b.Property("IdxFileAttach") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("IdxFileAttach"), 1L, 1); + + b.Property("DtEvent") + .HasColumnType("datetime2"); + + b.Property("FullStoragePath") + .HasColumnType("nvarchar(max)"); + + b.Property("IdxTicket") + .HasColumnType("int"); + + b.Property("OriginalName") + .HasColumnType("nvarchar(max)"); + + b.Property("StorageName") + .HasColumnType("nvarchar(max)"); + + b.HasKey("IdxFileAttach"); + + b.HasIndex("IdxTicket"); + + b.ToTable("FileAttach"); + }); + + modelBuilder.Entity("LiMan.DB.DBModels.InstallazioneModel", b => + { + b.Property("CodInst") + .HasMaxLength(50) + .HasColumnType("nvarchar(50)"); + + b.Property("Cliente") + .HasMaxLength(250) + .HasColumnType("nvarchar(250)"); + + b.Property("Contatto") + .HasMaxLength(250) + .HasColumnType("nvarchar(250)"); + + b.Property("Descrizione") + .HasMaxLength(250) + .HasColumnType("nvarchar(250)"); + + b.Property("Email") + .HasMaxLength(250) + .HasColumnType("nvarchar(250)"); + + b.HasKey("CodInst"); + + b.ToTable("Installazioni"); + }); + + modelBuilder.Entity("LiMan.DB.DBModels.LicenzaModel", b => + { + b.Property("IdxLic") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("IdxLic"), 1L, 1); + + b.Property("Chiave") + .HasColumnType("nvarchar(max)"); + + b.Property("CodApp") + .HasColumnType("nvarchar(50)"); + + b.Property("CodInst") + .HasColumnType("nvarchar(50)"); + + b.Property("DataEnigma") + .HasColumnType("datetime2"); + + b.Property("Descrizione") + .HasColumnType("nvarchar(max)"); + + b.Property("Enigma") + .HasColumnType("nvarchar(max)"); + + b.Property("Locked") + .HasColumnType("bit"); + + b.Property("NumLicenze") + .HasColumnType("int"); + + b.Property("Payload") + .HasColumnType("nvarchar(max)"); + + b.Property("Scadenza") + .HasColumnType("datetime2"); + + b.Property("Tipo") + .HasColumnType("int"); + + b.HasKey("IdxLic"); + + b.HasIndex("CodApp"); + + b.HasIndex("CodInst"); + + b.ToTable("Licenze"); + }); + + modelBuilder.Entity("LiMan.DB.DBModels.LogCallModel", b => + { + b.Property("DataRif") + .HasColumnType("datetime2"); + + b.Property("CodInst") + .HasColumnType("nvarchar(450)"); + + b.Property("CodApp") + .HasColumnType("nvarchar(450)"); + + b.Property("TargetUrl") + .HasColumnType("nvarchar(450)"); + + b.Property("NumCall") + .HasColumnType("int"); + + b.HasKey("DataRif", "CodInst", "CodApp", "TargetUrl"); + + b.ToTable("LogCall"); + }); + + modelBuilder.Entity("LiMan.DB.DBModels.LogLicenzaModel", b => + { + b.Property("IdxLogLic") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("IdxLogLic"), 1L, 1); + + b.Property("Chiave") + .HasColumnType("nvarchar(max)"); + + b.Property("CodApp") + .HasColumnType("nvarchar(50)"); + + b.Property("CodInst") + .HasColumnType("nvarchar(50)"); + + b.Property("Descrizione") + .HasColumnType("nvarchar(max)"); + + b.Property("IdxLic") + .HasColumnType("int"); + + b.Property("NumLicenze") + .HasColumnType("int"); + + b.Property("Scadenza") + .HasColumnType("datetime2"); + + b.Property("Tipo") + .HasColumnType("int"); + + b.HasKey("IdxLogLic"); + + b.HasIndex("CodApp"); + + b.HasIndex("CodInst"); + + b.HasIndex("IdxLic"); + + b.ToTable("LogLicenze"); + }); + + modelBuilder.Entity("LiMan.DB.DBModels.ReleaseModel", b => + { + b.Property("IdxRel") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("IdxRel"), 1L, 1); + + b.Property("CodApp") + .HasColumnType("nvarchar(50)"); + + b.Property("RelTags") + .HasColumnType("nvarchar(max)"); + + b.Property("ReleaseDate") + .HasColumnType("datetime2"); + + b.Property("VersNum") + .HasColumnType("nvarchar(max)"); + + b.Property("VersText") + .HasColumnType("nvarchar(max)"); + + b.HasKey("IdxRel"); + + b.HasIndex("CodApp"); + + b.ToTable("Releases"); + }); + + modelBuilder.Entity("LiMan.DB.DBModels.StatsCallModel", b => + { + b.Property("YearRef") + .HasColumnType("int"); + + b.Property("CodInst") + .HasColumnType("nvarchar(450)"); + + b.Property("CodApp") + .HasColumnType("nvarchar(450)"); + + b.Property("TotCall") + .HasColumnType("int"); + + b.HasKey("YearRef", "CodInst", "CodApp"); + + b.ToView("v_StatsCall"); + }); + + modelBuilder.Entity("LiMan.DB.DBModels.SubLicenzaModel", b => + { + b.Property("IdxSubLic") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("IdxSubLic"), 1L, 1); + + b.Property("Chiave") + .HasColumnType("nvarchar(max)"); + + b.Property("CodImpiego") + .HasColumnType("nvarchar(max)"); + + b.Property("IdxLic") + .HasColumnType("int"); + + b.Property("Tipo") + .HasColumnType("int"); + + b.Property("VetoUnlock") + .HasColumnType("datetime2"); + + b.HasKey("IdxSubLic"); + + b.HasIndex("IdxLic"); + + b.ToTable("SubLicenze"); + }); + + modelBuilder.Entity("LiMan.DB.DBModels.TicketModel", b => + { + b.Property("IdxTicket") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("IdxTicket"), 1L, 1); + + b.Property("CodImpiego") + .HasColumnType("nvarchar(max)"); + + b.Property("ContactEmail") + .HasColumnType("nvarchar(max)"); + + b.Property("ContactName") + .HasColumnType("nvarchar(max)"); + + b.Property("ContactPhone") + .HasColumnType("nvarchar(max)"); + + b.Property("DtReq") + .HasColumnType("datetime2"); + + b.Property("IdxLic") + .HasColumnType("int"); + + b.Property("IdxSubLic") + .HasColumnType("int"); + + b.Property("ReqBody") + .HasColumnType("nvarchar(max)"); + + b.Property("Status") + .HasColumnType("int"); + + b.Property("SupplAnsw") + .HasColumnType("nvarchar(max)"); + + b.Property("SupplEmail") + .HasColumnType("nvarchar(max)"); + + b.Property("SupplUserCode") + .HasColumnType("nvarchar(max)"); + + b.Property("TType") + .HasColumnType("int"); + + b.Property("Tipo") + .HasColumnType("int"); + + b.HasKey("IdxTicket"); + + b.HasIndex("IdxLic"); + + b.ToTable("TicketLog"); + }); + + modelBuilder.Entity("LiMan.DB.DBModels.AuthClaimModel", b => + { + b.HasOne("LiMan.DB.DBModels.AuthRoleModel", "RoleNav") + .WithMany("Claims") + .HasForeignKey("RoleID") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("LiMan.DB.DBModels.AuthUserModel", "UserNav") + .WithMany("Claims") + .HasForeignKey("UserID") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("RoleNav"); + + b.Navigation("UserNav"); + }); + + modelBuilder.Entity("LiMan.DB.DBModels.FileAttachModel", b => + { + b.HasOne("LiMan.DB.DBModels.TicketModel", "TicketNav") + .WithMany() + .HasForeignKey("IdxTicket") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("TicketNav"); + }); + + modelBuilder.Entity("LiMan.DB.DBModels.LicenzaModel", b => + { + b.HasOne("LiMan.DB.DBModels.ApplicativoModel", "ApplicativoNav") + .WithMany() + .HasForeignKey("CodApp"); + + b.HasOne("LiMan.DB.DBModels.InstallazioneModel", "InstallazioneNav") + .WithMany() + .HasForeignKey("CodInst"); + + b.Navigation("ApplicativoNav"); + + b.Navigation("InstallazioneNav"); + }); + + modelBuilder.Entity("LiMan.DB.DBModels.LogLicenzaModel", b => + { + b.HasOne("LiMan.DB.DBModels.ApplicativoModel", "ApplicativoNav") + .WithMany() + .HasForeignKey("CodApp"); + + b.HasOne("LiMan.DB.DBModels.InstallazioneModel", "InstallazioneNav") + .WithMany() + .HasForeignKey("CodInst"); + + b.HasOne("LiMan.DB.DBModels.LicenzaModel", "LicenzaNav") + .WithMany() + .HasForeignKey("IdxLic") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("ApplicativoNav"); + + b.Navigation("InstallazioneNav"); + + b.Navigation("LicenzaNav"); + }); + + modelBuilder.Entity("LiMan.DB.DBModels.ReleaseModel", b => + { + b.HasOne("LiMan.DB.DBModels.ApplicativoModel", "ApplicativoNav") + .WithMany() + .HasForeignKey("CodApp"); + + b.Navigation("ApplicativoNav"); + }); + + modelBuilder.Entity("LiMan.DB.DBModels.SubLicenzaModel", b => + { + b.HasOne("LiMan.DB.DBModels.LicenzaModel", "LicenzaNav") + .WithMany("Attivazioni") + .HasForeignKey("IdxLic") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("LicenzaNav"); + }); + + modelBuilder.Entity("LiMan.DB.DBModels.TicketModel", b => + { + b.HasOne("LiMan.DB.DBModels.LicenzaModel", "LicenzaNav") + .WithMany("Tickets") + .HasForeignKey("IdxLic") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("LicenzaNav"); + }); + + modelBuilder.Entity("LiMan.DB.DBModels.AuthRoleModel", b => + { + b.Navigation("Claims"); + }); + + modelBuilder.Entity("LiMan.DB.DBModels.AuthUserModel", b => + { + b.Navigation("Claims"); + }); + + modelBuilder.Entity("LiMan.DB.DBModels.LicenzaModel", b => + { + b.Navigation("Attivazioni"); + + b.Navigation("Tickets"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/LiMan.DB/Migrations/20241231105435_AddEnrollRequest.cs b/LiMan.DB/Migrations/20241231105435_AddEnrollRequest.cs new file mode 100644 index 0000000..f861641 --- /dev/null +++ b/LiMan.DB/Migrations/20241231105435_AddEnrollRequest.cs @@ -0,0 +1,37 @@ +using System; +using Microsoft.EntityFrameworkCore.Migrations; + +#nullable disable + +namespace LiMan.DB.Migrations +{ + public partial class AddEnrollRequest : Migration + { + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.CreateTable( + name: "EnrollRequest", + columns: table => new + { + IdReq = table.Column(type: "int", nullable: false) + .Annotation("SqlServer:Identity", "1, 1"), + Passcode = table.Column(type: "int", nullable: false), + ReqPayload = table.Column(type: "nvarchar(max)", nullable: true), + DtReq = table.Column(type: "datetime2", nullable: false), + DtAppr = table.Column(type: "datetime2", nullable: true), + UserAppr = table.Column(type: "nvarchar(max)", nullable: true), + IdxLic = table.Column(type: "int", nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_EnrollRequest", x => x.IdReq); + }); + } + + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropTable( + name: "EnrollRequest"); + } + } +} diff --git a/LiMan.DB/Migrations/LMDbContextModelSnapshot.cs b/LiMan.DB/Migrations/LMDbContextModelSnapshot.cs index 6857136..fb4f44c 100644 --- a/LiMan.DB/Migrations/LMDbContextModelSnapshot.cs +++ b/LiMan.DB/Migrations/LMDbContextModelSnapshot.cs @@ -121,6 +121,37 @@ namespace LiMan.DB.Migrations b.ToTable("AuthUsers"); }); + modelBuilder.Entity("LiMan.DB.DBModels.EnrollRequestModel", b => + { + b.Property("IdReq") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("IdReq"), 1L, 1); + + b.Property("DtAppr") + .HasColumnType("datetime2"); + + b.Property("DtReq") + .HasColumnType("datetime2"); + + b.Property("IdxLic") + .HasColumnType("int"); + + b.Property("Passcode") + .HasColumnType("int"); + + b.Property("ReqPayload") + .HasColumnType("nvarchar(max)"); + + b.Property("UserAppr") + .HasColumnType("nvarchar(max)"); + + b.HasKey("IdReq"); + + b.ToTable("EnrollRequest"); + }); + modelBuilder.Entity("LiMan.DB.DBModels.FileAttachModel", b => { b.Property("IdxFileAttach") diff --git a/LiMan.Transfer/Resources/ChangeLog.html b/LiMan.Transfer/Resources/ChangeLog.html index 83d93bd..b2e2085 100644 --- a/LiMan.Transfer/Resources/ChangeLog.html +++ b/LiMan.Transfer/Resources/ChangeLog.html @@ -1,6 +1,6 @@ License Manager -

    Versione: 1.1.2410.1618

    +

    Versione: 1.1.2501.0409


    Note di rilascio:
      diff --git a/LiMan.Transfer/Resources/VersNum.txt b/LiMan.Transfer/Resources/VersNum.txt index 17444b0..6a1a7a8 100644 --- a/LiMan.Transfer/Resources/VersNum.txt +++ b/LiMan.Transfer/Resources/VersNum.txt @@ -1 +1 @@ -1.1.2410.1618 +1.1.2501.0409 diff --git a/LiMan.Transfer/Resources/manifest.xml b/LiMan.Transfer/Resources/manifest.xml index f40945f..31b4bc4 100644 --- a/LiMan.Transfer/Resources/manifest.xml +++ b/LiMan.Transfer/Resources/manifest.xml @@ -1,6 +1,6 @@ - 1.1.2410.1618 + 1.1.2501.0409 https://nexus.steamware.net/repository/SWS/LiMan/stable/LAST/LiMan.Transfer.zip https://nexus.steamware.net/repository/SWS/LiMan/stable/LAST/ChangeLog.html false diff --git a/LiMan.UI/Components/Activations.razor.cs b/LiMan.UI/Components/Activations.razor.cs index 2bdf421..1d6a42f 100644 --- a/LiMan.UI/Components/Activations.razor.cs +++ b/LiMan.UI/Components/Activations.razor.cs @@ -189,7 +189,7 @@ namespace LiMan.UI.Components ListRecords = null; await Task.Delay(1); ListRecords = MasterLicence.Attivazioni.ToList(); - //AllRecords = await DataService.AttivazioniGetByLic(MasterLicence.IdxLic); + //SearchRecords = await LMDService.AttivazioniGetByLic(MasterLicence.IdxLic); await Task.Delay(1); isLoading = false; } diff --git a/LiMan.UI/Components/CadCamSearchLic.razor.cs b/LiMan.UI/Components/CadCamSearchLic.razor.cs index ce7932f..d3f5207 100644 --- a/LiMan.UI/Components/CadCamSearchLic.razor.cs +++ b/LiMan.UI/Components/CadCamSearchLic.razor.cs @@ -13,13 +13,16 @@ namespace LiMan.UI.Components #region Public Properties [Parameter] - public string searchVal { get; set; } = ""; + public EventCallback EC_LockIdSel { get; set; } + [Parameter] + public string LockId { get; set; } = ""; [Parameter] public int ProdId { get; set; } = 0; + [Parameter] - public string LockId { get; set; } = ""; + public string searchVal { get; set; } = ""; #endregion Public Properties @@ -30,21 +33,6 @@ namespace LiMan.UI.Components #endregion Protected Properties - #region Private Properties - - private List AllRecords { get; set; } = new List(); - - private int currPage { get; set; } = 1; - - private bool isLoading { get; set; } = false; - - private List ListRecords { get; set; } = new List(); - private int numRecord { get; set; } = 10; - private List SearchRecords { get; set; } = new List(); - private int totalCount { get; set; } = 0; - - #endregion Private Properties - #region Protected Methods protected override async Task OnInitializedAsync() @@ -58,6 +46,11 @@ namespace LiMan.UI.Components // aggiorno } + protected async Task ReportLockId(string lockId) + { + await EC_LockIdSel.InvokeAsync(lockId); + } + protected void setNumPage(int newNum) { currPage = newNum; @@ -74,16 +67,23 @@ namespace LiMan.UI.Components #endregion Protected Methods + #region Private Properties + + private List AllRecords { get; set; } = new List(); + + private int currPage { get; set; } = 1; + + private bool isLoading { get; set; } = false; + + private List ListRecords { get; set; } = new List(); + private int numRecord { get; set; } = 10; + private List SearchRecords { get; set; } = new List(); + private int totalCount { get; set; } = 0; + + #endregion Private Properties + #region Private Methods - protected async Task ReportLockId(string lockId) - { - await EC_LockIdSel.InvokeAsync(lockId); - } - - [Parameter] - public EventCallback EC_LockIdSel { get; set; } - private void ReloadAllData() { // rileggo i dati @@ -91,8 +91,8 @@ namespace LiMan.UI.Components if (!string.IsNullOrEmpty(searchVal) || !string.IsNullOrEmpty(LockId)) { SearchRecords = AllRecords - .Where(x => (ProdId == 0 || x.ProductID == ProdId) && (string.IsNullOrEmpty(LockId) || x.LockID.Contains(LockId, StringComparison.CurrentCultureIgnoreCase)) - && (string.IsNullOrEmpty(searchVal) || ( + .Where(x => (ProdId == 0 || x.ProductID == ProdId) && (string.IsNullOrEmpty(LockId) || x.LockID.Contains(LockId, StringComparison.CurrentCultureIgnoreCase)) + && (string.IsNullOrEmpty(searchVal) || ( (!string.IsNullOrEmpty(x.Note) && x.Note.Contains(searchVal, StringComparison.CurrentCultureIgnoreCase)) || x.ProductVersion.ToString().Contains(searchVal, StringComparison.CurrentCultureIgnoreCase) || (!string.IsNullOrEmpty(x.Note) && x.Note.Contains(searchVal, StringComparison.CurrentCultureIgnoreCase)) diff --git a/LiMan.UI/Components/EnrollList.razor b/LiMan.UI/Components/EnrollList.razor new file mode 100644 index 0000000..2f0d968 --- /dev/null +++ b/LiMan.UI/Components/EnrollList.razor @@ -0,0 +1,233 @@ +@if (isLoading) +{ + +} +else if (SelRecord != null) +{ +
      +
      +
      +
      +

      @($"{SelRecord.Passcode:00 00 00 00}")

      +
      +
      +
        + @foreach (var kvp in SelRecord.DictAttrib) + { +
      • @kvp.Key : @kvp.Value
      • + } +
      +
      +
      +
      +
      + @if (ShowAddLic) + { + + } + else if (ShowEditLic) + { + + } +
      +
      +

      Assegnazione licenza

      +
      +
      + @if (SelRecord.IdxLic == 0) + { + + } + + +
      +
      +
      +
      + @if (SelRecord.IdxLic == 0) + { +
      + + +
      + + @if (string.IsNullOrEmpty(SelRecord.UserAppr) && SelIdxLic > 0) + { + + } + } + else + { +
      + Licenza asegnata: @SelIdxLic +
      + } +
      +
      +
      +
      +} +else if (SearchRecords == null || SearchRecords.Count == 0) +{ +
      No record Found
      +} +else +{ +
      + @foreach (var item in ListRecords) + { +
      +
      +
      +

      @($"{item.Passcode:00 00 00 00}")

      +
      +
      +
        +
      • +
        + Richiesta: +
        +
        + @($"{item.DtReq:ddd yyyy-MM-dd, HH:MM:ss}") +
        +
      • + @if (item.DictNumKVP() > nShort) + { + + @foreach (var kvp in item.DictAttribShort(nShort)) + { +
      • +
        @kvp.Key:
        +
        @kvp.Value
        +
      • + } +
      • +
        + @if (string.IsNullOrEmpty(item.UserAppr)) + { + + } + else + { + + } +
        +
        ...@(item.DictNumKVP() - nShort) more
        +
      • + } + else + { + @foreach (var kvp in item.DictAttrib) + { +
      • +
        @kvp.Key:
        +
        @kvp.Value
        +
      • + } + } +
      +
      +
      +
      + } +
      +
      +
      + +
      +
      +} diff --git a/LiMan.UI/Components/EnrollList.razor.cs b/LiMan.UI/Components/EnrollList.razor.cs new file mode 100644 index 0000000..a0ee72c --- /dev/null +++ b/LiMan.UI/Components/EnrollList.razor.cs @@ -0,0 +1,281 @@ +using Liman.CadCam.Services; +using LiMan.DB; +using LiMan.DB.DBModels; +using LiMan.UI.Data; +using Microsoft.AspNetCore.Components; +using Microsoft.AspNetCore.Components.Authorization; +using Microsoft.JSInterop; +using Newtonsoft.Json; +using NLog.LayoutRenderers; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using static Core.Enum; + +namespace LiMan.UI.Components +{ + public partial class EnrollList : IDisposable + { + #region Public Properties + + [Parameter] + public DateTime DtEnd { get; set; } = DateTime.Today.AddDays(1); + + [Parameter] + public DateTime DtStart { get; set; } = DateTime.Today.AddMonths(-1); + + [Parameter] + public bool OnlyActive { get; set; } = true; + + [Parameter] + public string SearchVal { get; set; } = ""; + + #endregion Public Properties + + #region Public Methods + + /// + /// Metodo dispose + /// + public virtual void Dispose() + { + //LMDService.EnrollMessPipe.EA_NewMessage -= EnrollMessPipe_EA_NewMessage; + LMDService.EnrollMessPipe.EA_NewMessage -= async (sender, e) => await EnrollMessPipe_EA_NewMessage(sender, e); + } + + #endregion Public Methods + + #region Protected Properties + + [Inject] + protected AuthenticationStateProvider AuthStateProvider { get; set; } = null!; + + [Inject] + protected LiManDataService LMDService { get; set; } = null!; + + #endregion Protected Properties + + #region Protected Methods + + /// + /// Aggiunge la licenza selezionata x 10 slot... + /// + /// + protected async Task AddLicense() + { + // chiedo conferma... + if (!await JSRuntime.InvokeAsync("confirm", $"Sicuro di voler aggiungere la richiesta per l'installazione {SelCodInst}? L'operazione non è reversibile")) + return; + + LicenzaModel newLic = new LicenzaModel() + { + CodApp = "UpdateManager", + CodInst = SelCodInst, + NumLicenze = 10, + Tipo = TipoLicenza.MasterKey, + Scadenza = new DateTime(2099, 12, 31), + Descrizione = "Licenza per Richieste Enroll Applicativi", + Enigma = "", + Payload = "", + }; + // aggiungo chiave calcolata + newLic.Chiave = newLic.ChiaveCalc; + await LMDService.LicenzeNextUpdate(newLic); + ShowAddLic = false; + ShowEditLic = false; + await ReloadLicData(); + } + + /// + /// Aggiunge all licenza selezionata altri 10 slot... + /// + /// + protected async Task AddActivations() + { + // chiedo conferma... + if (!await JSRuntime.InvokeAsync("confirm", $"Sicuro di voler aggiungere 10 ulteriori slot alla licenza {SelCodInst}? L'operazione non è reversibile")) + return; + + var currLic = LMDService.LicenzaNextGetByIdx(SelIdxLic); + currLic.NumLicenze += 10; + // aggiorno chiave calcolata + currLic.Chiave = currLic.ChiaveCalc; + await LMDService.LicenzeNextUpdate(currLic); + ShowAddLic = false; + ShowEditLic = false; + await ReloadLicData(); + } + + protected async Task DoApprove() + { + // chiedo conferma... + if (!await JSRuntime.InvokeAsync("confirm", "Sicuro di voler confermare la richiesta?")) + return; + + var authState = await AuthStateProvider.GetAuthenticationStateAsync(); + var user = authState.User; + + if (user.Identity.IsAuthenticated && !string.IsNullOrEmpty(user.Identity.Name)) + { + // registra approvazione + SelRecord.DtAppr = DateTime.Now; + SelRecord.UserAppr = user.Identity.Name; + SelRecord.IdxLic = SelIdxLic; + await LMDService.EnrollReqUpsert(SelRecord); + SelRecord = null; + await ReloadData(); + } + } + + protected async Task DoSelect(EnrollRequestModel curRec) + { + SelRecord = curRec; + await ReloadLicData(); + } + + /// + /// init sottoscrizione messaggi + refresh dati + /// + /// + protected override async Task OnInitializedAsync() + { + LMDService.EnrollMessPipe.EA_NewMessage += async (sender, e) => await EnrollMessPipe_EA_NewMessage(sender, e); + await ReloadData(); + } + + protected override async Task OnParametersSetAsync() + { + await ReloadData(); + } + + protected async Task ResetSel() + { + SelRecord = null; + await ReloadData(); + } + + protected async Task setNumPage(int newNum) + { + currPage = newNum; + await ReloadData(); + isLoading = false; + } + + protected async Task setNumRec(int newNum) + { + numRecord = newNum; + await ReloadData(); + isLoading = false; + } + + protected void ToggleAddNew() + { + ShowAddLic = !ShowAddLic; + } + protected void ToggleEditLic() + { + ShowEditLic = !ShowEditLic; + } + + #endregion Protected Methods + + #region Private Fields + + private List ListInstall; + private List ListLicenze; + + /// + /// num max di dettagli KVP da mostrare + /// + private int nShort = 2; + + private List PageList = new List() { 4, 8, 12, 16, 24, 48 }; + + /// + /// Codice installazione selezionata + /// + private string SelCodInst = ""; + + /// + /// Idx licenza selezionata + /// + private int SelIdxLic = 0; + + private bool ShowAddLic = false; + private bool ShowEditLic = false; + + #endregion Private Fields + + #region Private Properties + + private int currPage { get; set; } = 1; + + private bool isLoading { get; set; } = false; + + [Inject] + private IJSRuntime JSRuntime { get; set; } + + private List ListRecords { get; set; } = new List(); + + private int numRecord { get; set; } = 8; + + private List SearchRecords { get; set; } = new List(); + + private EnrollRequestModel? SelRecord { get; set; } = null; + + private int totalCount { get; set; } = 0; + + #endregion Private Properties + + #region Private Methods + + /// + /// Evento refresh legato a ricezione evento da MessagePipe + /// + /// + /// + private async Task EnrollMessPipe_EA_NewMessage(object sender, EventArgs e) + { + PubSubEventArgs currArgs = (PubSubEventArgs)e; + // qualsiasi messaggio fa scattare reload data + if (!string.IsNullOrEmpty(currArgs.newMessage)) + { + isLoading = true; + //await InvokeAsync(StateHasChanged); + await LMDService.FlushEnrollCache(); + //await Task.Delay(50); + await ReloadData(); + await InvokeAsync(StateHasChanged); + } + } + + private async Task ReloadData() + { + isLoading = true; + SearchRecords = await LMDService.EnrollReqGetFilt(OnlyActive, DtStart, DtEnd); + if (!string.IsNullOrEmpty(SearchVal)) + { + SearchRecords = SearchRecords.Where(x => $"{x.Passcode}".Contains(SearchVal)).ToList(); + } + totalCount = SearchRecords.Count; + ListRecords = SearchRecords + .Skip((currPage - 1) * numRecord) + .Take(numRecord) + .ToList(); + isLoading = false; + } + + private async Task ReloadLicData() + { + SelectNext currFilt = new SelectNext() + { + ApplicazioneSel = "UpdateManager" + }; + ListLicenze = await LMDService.LicenzeNextGetFilt(currFilt); + ListInstall = await LMDService.InstallazioniNextGetAll(); + } + + #endregion Private Methods + } +} \ No newline at end of file diff --git a/LiMan.UI/Components/ListApplicazioni.razor.cs b/LiMan.UI/Components/ListApplicazioni.razor.cs index f074d38..920c85a 100644 --- a/LiMan.UI/Components/ListApplicazioni.razor.cs +++ b/LiMan.UI/Components/ListApplicazioni.razor.cs @@ -289,7 +289,7 @@ namespace LiMan.UI.Components { isLoading = true; - //AllRecords = null; + //SearchRecords = null; SearchRecords = await DataService.ApplicazioniNextGetAll(); if (!string.IsNullOrEmpty(appTipoSel)) { diff --git a/LiMan.UI/Components/ListApplicazioniGLS.razor.cs b/LiMan.UI/Components/ListApplicazioniGLS.razor.cs index 663a01c..8efdc1c 100644 --- a/LiMan.UI/Components/ListApplicazioniGLS.razor.cs +++ b/LiMan.UI/Components/ListApplicazioniGLS.razor.cs @@ -86,7 +86,7 @@ namespace LiMan.UI.Components { isLoading = true; await Task.Delay(1); - //AllRecords = null; + //SearchRecords = null; SearchRecords = await DataService.ApplicazioniGLSGetAll(); totalCount = SearchRecords.Count(); ListRecords = SearchRecords.Skip(numRecord * (currPage - 1)).Take(numRecord).ToList(); diff --git a/LiMan.UI/Components/ListInstallazioni.razor.cs b/LiMan.UI/Components/ListInstallazioni.razor.cs index cf6da4b..4c8dced 100644 --- a/LiMan.UI/Components/ListInstallazioni.razor.cs +++ b/LiMan.UI/Components/ListInstallazioni.razor.cs @@ -86,7 +86,7 @@ namespace LiMan.UI.Components { isLoading = true; await Task.Delay(1); - //AllRecords = null; + //SearchRecords = null; SearchRecords = await DataService.InstallazioniNextGetAll(); totalCount = SearchRecords.Count(); ListRecords = SearchRecords.Skip(numRecord * (currPage - 1)).Take(numRecord).ToList(); diff --git a/LiMan.UI/Components/ListInstallazioniGLS.razor.cs b/LiMan.UI/Components/ListInstallazioniGLS.razor.cs index 6ca30e0..53a702d 100644 --- a/LiMan.UI/Components/ListInstallazioniGLS.razor.cs +++ b/LiMan.UI/Components/ListInstallazioniGLS.razor.cs @@ -86,7 +86,7 @@ namespace LiMan.UI.Components { isLoading = true; await Task.Delay(1); - //AllRecords = null; + //SearchRecords = null; SearchRecords = await DataService.InstallazioniGLSGetAll(); totalCount = SearchRecords.Count(); ListRecords = SearchRecords.Skip(numRecord * (currPage - 1)).Take(numRecord).ToList(); diff --git a/LiMan.UI/Components/ListLicenze.razor.cs b/LiMan.UI/Components/ListLicenze.razor.cs index a197fc2..8e3499e 100644 --- a/LiMan.UI/Components/ListLicenze.razor.cs +++ b/LiMan.UI/Components/ListLicenze.razor.cs @@ -126,13 +126,14 @@ namespace LiMan.UI.Components CodApp = "AAA", CodInst = "EgalWare", Descrizione = "Nuova Licenza", - Chiave = "000", NumLicenze = 1, Enigma = "", Payload = "", Tipo = TipoLicenza.MasterKey, Scadenza = DateTime.Today.AddMonths(1) }; + // calcolo chiave ed aggiungo... + newRec.Chiave = newRec.ChiaveCalc; currRecord = newRec; resetShowDetail(); } diff --git a/LiMan.UI/Components/SearchEnroll.razor b/LiMan.UI/Components/SearchEnroll.razor new file mode 100644 index 0000000..aa0a693 --- /dev/null +++ b/LiMan.UI/Components/SearchEnroll.razor @@ -0,0 +1,59 @@ + +
      +
      +
      +
      +
      +

      Richieste Enroll

      +
      +
      + +
      + @if (hasUpdate) + { +
      + Enroll Updated! +
      + } +
      +
      +
      +
      +
      + + +
      +
      + @*
      +
      + Prod + +
      +
      +
      +
      + Key + + +
      +
      *@ +
      + +
      +
      +
      +
      +
      +
      + +
      +
      diff --git a/LiMan.UI/Components/SearchEnroll.razor.cs b/LiMan.UI/Components/SearchEnroll.razor.cs new file mode 100644 index 0000000..6250dd7 --- /dev/null +++ b/LiMan.UI/Components/SearchEnroll.razor.cs @@ -0,0 +1,128 @@ +using Liman.CadCam.DbModel; +using Liman.CadCam.Services; +using LiMan.UI.Data; +using Microsoft.AspNetCore.Components; +using System.Collections.Generic; +using System.Threading.Tasks; +using System; +using System.Linq; +using LiMan.DB; + +namespace LiMan.UI.Components +{ + public partial class SearchEnroll : IDisposable + { + #region Public Properties + + public string searchVal { get; set; } = ""; + + #endregion Public Properties + + #region Public Methods + + public void Dispose() + { + AppMessages.EA_SearchUpdated -= AppMessages_EA_SearchUpdated; + LMDService.EnrollMessPipe.EA_NewMessage -= async (sender, e) => await EnrollMessPipe_EA_NewMessage(sender, e); + } + + #endregion Public Methods + + #region Protected Properties + + [Inject] + protected MessageService AppMessages { get; set; } = null!; + + [Inject] + protected LiManDataService LMDService { get; set; } = null!; + + #endregion Protected Properties + + #region Protected Methods + + protected override async Task OnInitializedAsync() + { + AppMessages.EA_SearchUpdated += AppMessages_EA_SearchUpdated; + LMDService.EnrollMessPipe.EA_NewMessage += async (sender, e) => await EnrollMessPipe_EA_NewMessage(sender, e); + await Task.Delay(1); + } + + /// + /// Rimozione richieste scadute + /// + /// + protected async Task RemoveOld() + { + await LMDService.EnrollReqPurgeInvalid(); + await LMDService.FlushEnrollCache(); + searchVal = ""; + } + + #endregion Protected Methods + + #region Private Fields + + private bool isLoading = false; + + /// + /// Indica se mostrare tutte le richieste o solo quelle Attive(aperte/non confermate) + /// + private bool showAll = false; + + #endregion Private Fields + + #region Private Properties + + private bool hasUpdate { get; set; } = false; + private List ListProd { get; set; } = new List(); + + /// + /// Testo associato al toogle button delle richieste Attive / Tutte + /// + private string selMessage + { + get => showAll ? "Mostra tutte" : "Solo Attive"; + } + + #endregion Private Properties + + #region Private Methods + + private void AppMessages_EA_SearchUpdated() + { + ReloadData(); + } + + /// + /// Evento refresh legato a ricezione evento da MessagePipe + /// + /// + /// + private async Task EnrollMessPipe_EA_NewMessage(object sender, EventArgs e) + { + PubSubEventArgs currArgs = (PubSubEventArgs)e; + // qualsiasi messaggio fa scattare reload data + if (!string.IsNullOrEmpty(currArgs.newMessage)) + { + hasUpdate = true; + await InvokeAsync(StateHasChanged); + await Task.Delay(2500); + hasUpdate = false; + await InvokeAsync(StateHasChanged); + } + } + + private void ReloadData() + { + isLoading = true; + if (searchVal != AppMessages.SearchVal) + { + searchVal = AppMessages.SearchVal; + } + isLoading = false; + InvokeAsync(StateHasChanged); + } + + #endregion Private Methods + } +} \ No newline at end of file diff --git a/LiMan.UI/Components/SearchMod.razor b/LiMan.UI/Components/SearchMod.razor index e8c0e54..3712ffc 100644 --- a/LiMan.UI/Components/SearchMod.razor +++ b/LiMan.UI/Components/SearchMod.razor @@ -2,7 +2,7 @@ @using LiMan.UI.Data @inject MessageService MessageService -
      +
      @@ -26,6 +26,14 @@ } } + + [Parameter] + public string ExtCss + { + get => extCss; + set => extCss = value; + } + private void reportChange() { searchUpdated.InvokeAsync(searchVal); @@ -36,4 +44,6 @@ searchVal = ""; } + private string extCss = "input-group input-group-sm"; + } \ No newline at end of file diff --git a/LiMan.UI/Components/StatsCallList.razor.cs b/LiMan.UI/Components/StatsCallList.razor.cs index 65f801b..f9ece43 100644 --- a/LiMan.UI/Components/StatsCallList.razor.cs +++ b/LiMan.UI/Components/StatsCallList.razor.cs @@ -178,7 +178,7 @@ namespace LiMan.UI.Components { isLoading = true; await Task.Delay(1); - //AllRecords = null; + //SearchRecords = null; SearchRecords = await DataService.StatsLogCallGetFilt(dtFrom, dtTo, searchVal); totalCount = SearchRecords.Count(); ListRecords = SearchRecords.Skip(numRecord * (currPage - 1)).Take(numRecord).ToList(); diff --git a/LiMan.UI/Components/Tickets.razor.cs b/LiMan.UI/Components/Tickets.razor.cs index 68429ed..a4477fb 100644 --- a/LiMan.UI/Components/Tickets.razor.cs +++ b/LiMan.UI/Components/Tickets.razor.cs @@ -129,12 +129,12 @@ namespace LiMan.UI.Components ListApp = await DataService.ApplicazioniNextGetAll(); ListInstall = await DataService.InstallazioniNextGetAll(); //bool StatoRichiesta = true; - //SearchRecords = await DataService.LicenzeNextGetFilt(AppMServ.DetailDBFilter); + //SearchRecords = await LMDService.LicenzeNextGetFilt(AppMServ.DetailDBFilter); SearchRecords = await DataService.TicketsGetFilt(true, SelApp, SelInst); //totalCount = SearchRecords.Count(); - //AllRecords = SearchRecords.Skip(numRecord * (currPage - 1)).Take(numRecord).ToList(); + //SearchRecords = SearchRecords.Skip(numRecord * (currPage - 1)).Take(numRecord).ToList(); - //AllRecords = await DataService.TicketsGetAll(); + //SearchRecords = await LMDService.TicketsGetAll(); ListRecords = SearchRecords.Skip(numRecord * (currPage - 1)).Take(numRecord).ToList(); totalCount = SearchRecords.Count(); diff --git a/LiMan.UI/Components/TicketsHP.razor.cs b/LiMan.UI/Components/TicketsHP.razor.cs index 05bc6ed..456ac6a 100644 --- a/LiMan.UI/Components/TicketsHP.razor.cs +++ b/LiMan.UI/Components/TicketsHP.razor.cs @@ -171,7 +171,7 @@ namespace LiMan.UI.Components // idxTicketSel = currTicket.IdxTicket; // StatusSel = currTicket.Status; // currTipo = currTicket.TType; - // FileAttached = await DataService.FileGetFilt(idxTicketSel); + // FileAttached = await LMDService.FileGetFilt(idxTicketSel); // // mostro SOLO le attivazioni di cui ho ticket attivi... // ListActivations = MasterLicence // .Attivazioni diff --git a/LiMan.UI/Data/LiManDataService.cs b/LiMan.UI/Data/LiManDataService.cs index 7795896..975860e 100644 --- a/LiMan.UI/Data/LiManDataService.cs +++ b/LiMan.UI/Data/LiManDataService.cs @@ -1,6 +1,7 @@ using Core; using Core.DTO; using Liman.CadCam.DbModel; +using LiMan.DB; using LiMan.DB.Controllers; using LiMan.DB.DBModels; using LiMan.DB.DTO; @@ -50,6 +51,11 @@ namespace LiMan.UI.Data { ReferenceLoopHandling = ReferenceLoopHandling.Ignore }; + + + // conf messagepipe: setup canali pub/sub + EnrollMessPipe = new MessagePipe(redisConn, Const.ENR_MSG_PIPE); + _emailSender = emailSender; // conf cache this.memoryCache = memoryCache; @@ -114,47 +120,6 @@ namespace LiMan.UI.Data return await Task.FromResult(done); } - public async Task> ApplicazioniNextGetAll() - { - List dbResult = new List(); - string cacheKey = mHash("Next:Applicazioni"); - string rawData; - var redisDataList = await distributedCache.GetAsync(cacheKey); - if (redisDataList != null) - { - rawData = Encoding.UTF8.GetString(redisDataList); - dbResult = JsonConvert.DeserializeObject>(rawData); - } - else - { - Stopwatch stopWatch = new Stopwatch(); - stopWatch.Start(); - dbResult = dbControllerNext.GetApplicazioni(); - rawData = JsonConvert.SerializeObject(dbResult); - redisDataList = Encoding.UTF8.GetBytes(rawData); - await distributedCache.SetAsync(cacheKey, redisDataList, cacheOpt(true)); - stopWatch.Stop(); - TimeSpan ts = stopWatch.Elapsed; - Log.Trace($"Effettuata lettura da DB + caching per ApplicazioniNextGetAll: {ts.TotalMilliseconds} ms"); - } - return await Task.FromResult(dbResult); - } - - public async Task ApplicazioniNextUpdate(ApplicativoModel currItem) - { - bool done = false; - try - { - done = dbControllerNext.ApplicazioniNextUpdate(currItem); - await InvalidateAllCache(); - } - catch (Exception exc) - { - Log.Error($"Eccezione in ApplicazioniNextUpdate:{Environment.NewLine}{exc}"); - } - return done; - } - public async Task ApplicazioniHasChild(string CodApp) { bool dbResult = false; @@ -206,7 +171,46 @@ namespace LiMan.UI.Data return done; } + public async Task> ApplicazioniNextGetAll() + { + List dbResult = new List(); + string cacheKey = mHash("Next:Applicazioni"); + string rawData; + var redisDataList = await distributedCache.GetAsync(cacheKey); + if (redisDataList != null) + { + rawData = Encoding.UTF8.GetString(redisDataList); + dbResult = JsonConvert.DeserializeObject>(rawData); + } + else + { + Stopwatch stopWatch = new Stopwatch(); + stopWatch.Start(); + dbResult = dbControllerNext.GetApplicazioni(); + rawData = JsonConvert.SerializeObject(dbResult); + redisDataList = Encoding.UTF8.GetBytes(rawData); + await distributedCache.SetAsync(cacheKey, redisDataList, cacheOpt(true)); + stopWatch.Stop(); + TimeSpan ts = stopWatch.Elapsed; + Log.Trace($"Effettuata lettura da DB + caching per ApplicazioniNextGetAll: {ts.TotalMilliseconds} ms"); + } + return await Task.FromResult(dbResult); + } + public async Task ApplicazioniNextUpdate(ApplicativoModel currItem) + { + bool done = false; + try + { + done = dbControllerNext.ApplicazioniNextUpdate(currItem); + await InvalidateAllCache(); + } + catch (Exception exc) + { + Log.Error($"Eccezione in ApplicazioniNextUpdate:{Environment.NewLine}{exc}"); + } + return done; + } /// /// Effettua sblocco di una licenza impostando data veto a oggi @@ -584,6 +588,158 @@ namespace LiMan.UI.Data dbControllerGLS.Dispose(); } + /// + /// Recupera una richiesta dato suo ID x verificare approvazione e dati associati... + /// + /// ID record + public async Task EnrollReqGetById(int idReq) + { + string source = "DB"; + EnrollRequestModel dbResult = new EnrollRequestModel() { IdReq = idReq }; + try + { + string currKey = $"{Const.rKeyConfig}:Enroll:ById:{idReq}"; + Stopwatch sw = new Stopwatch(); + sw.Start(); + string? rawData = await redisDb.StringGetAsync(currKey); + if (!string.IsNullOrEmpty(rawData)) + { + source = "REDIS"; + var tempResult = JsonConvert.DeserializeObject(rawData); + if (tempResult == null) + { + dbResult = new EnrollRequestModel() { IdReq = idReq }; + } + else + { + dbResult = tempResult; + } + } + else + { + dbResult = dbControllerNext.EnrollReqGetById(idReq); + rawData = JsonConvert.SerializeObject(dbResult, JSSettings); + await redisDb.StringSetAsync(currKey, rawData, FastCache); + } + if (dbResult == null) + { + dbResult = new EnrollRequestModel() { IdReq = idReq }; + } + sw.Stop(); + TimeSpan ts = sw.Elapsed; + Log.Debug($"EnrollReqGetById | {source} in: {ts.TotalMilliseconds} ms"); + } + catch (Exception exc) + { + Log.Error($"Error during EnrollReqGetById:{Environment.NewLine}{exc}"); + } + return dbResult; + } + + /// + /// Elimino eventuali richieste non approvate e scadute + /// + public async Task EnrollReqPurgeInvalid() + { + var reqPurged = dbControllerNext.EnrollReqPurgeInvalid(); + // invio string in messagepipe x forzare refresh... + EnrollMessPipe.sendMessage("NewEnrollReq"); + // svuota eventuale cache redis... + await FlushRedisCachePattern("Enroll"); + return reqPurged; + } + + /// + /// Elenco richeiste enroll attive al momento + /// + /// + public async Task> EnrollReqGetFilt(bool onlyActive, DateTime dtFrom, DateTime dtTo) + { + string source = "DB"; + List dbResult = new List(); + try + { + string currKey = $"{Const.rKeyConfig}:Enroll:ActiveList"; + if (!onlyActive) + { + currKey = $"{Const.rKeyConfig}:Enroll:Sel:{dtFrom:yyMMdd}_{dtTo:yyMMdd}"; + } + Stopwatch sw = new Stopwatch(); + sw.Start(); + string? rawData = await redisDb.StringGetAsync(currKey); + if (!string.IsNullOrEmpty(rawData)) + { + source = "REDIS"; + var tempResult = JsonConvert.DeserializeObject>(rawData); + if (tempResult == null) + { + dbResult = new List(); + } + else + { + dbResult = tempResult; + } + } + else + { + if (onlyActive) + { + dbResult = dbControllerNext.EnrollReqGetActive(); + } + else + { + dbResult = dbControllerNext.EnrollReqGetFilt(dtFrom, dtTo); + } + rawData = JsonConvert.SerializeObject(dbResult, JSSettings); + await redisDb.StringSetAsync(currKey, rawData, UltraLongCache); + } + if (dbResult == null) + { + dbResult = new List(); + } + sw.Stop(); + TimeSpan ts = sw.Elapsed; + Log.Debug($"EnrollReqGetActive | {source} in: {ts.TotalMilliseconds} ms"); + } + catch (Exception exc) + { + Log.Error($"Error during EnrollReqGetActive:{Environment.NewLine}{exc}"); + } + return dbResult; + } + + /// + /// Upsert record richiesta enroll + /// + /// + /// + public async Task EnrollReqUpsert(EnrollRequestModel newRec) + { + int recId = 0; + // inserimento! + Stopwatch sw = new Stopwatch(); + sw.Start(); + recId = dbControllerNext.EnrollReqUpsert(newRec); + await FlushRedisCachePattern("Enroll"); + var dbResult = await EnrollReqGetById(recId); + // svuota eventuale cache redis... + sw.Stop(); + TimeSpan ts = sw.Elapsed; + Log.Trace($"Effettuata EnrollReqUpsert: {ts.TotalMilliseconds} ms"); + // restituisce risultato + return dbResult; + } + /// + /// Esegue flush cache dati enroll x successiva rilettura + /// + /// + /// + public async Task FlushEnrollCache() + { + bool fatto = await FlushRedisCachePattern("Enroll"); + return fatto; + } + /// /// Elenco file registrati dato ticket id /// @@ -618,6 +774,23 @@ namespace LiMan.UI.Data return answ; } + /// + /// Refresh cache Redis dato redPattern + /// + /// Pattern da eliminare + /// + public async Task FlushRedisCachePattern(string pattern) + { + Stopwatch sw = new Stopwatch(); + sw.Start(); + await Task.Delay(1); + RedisValue redPattern = new RedisValue($"{Const.rKeyConfig}:{pattern}*"); + bool answ = await ExecFlushRedisPattern(redPattern); + sw.Stop(); + Log.Debug($"FlushRedisCachePattern in {sw.Elapsed.TotalMilliseconds} ms"); + return answ; + } + public async Task> InstallazioniGLSGetAll() { List dbResult = new List(); @@ -906,6 +1079,23 @@ namespace LiMan.UI.Data return await Task.FromResult(dbResult); } + /// + /// Recupera licenza dato IDX + /// + /// + /// + public LicenzaModel LicenzaNextGetByIdx(int IdxLic) + { + Stopwatch sw = new Stopwatch(); + LicenzaModel dbResult = new LicenzaModel(); + sw.Start(); + dbResult = dbControllerNext.GetLicenza(IdxLic); + sw.Stop(); + TimeSpan ts = sw.Elapsed; + Log.Trace($"Effettuata lettura da DB per LicenzeNextGetByIdx: {ts.TotalMilliseconds} ms"); + return dbResult; + } + public async Task LicenzeNextUpdate(LicenzaModel currItem) { bool done = false; @@ -923,6 +1113,11 @@ namespace LiMan.UI.Data return await Task.FromResult(done); } + /// + /// Wrapper x invio/ricezione messaggi sul canale dedicato agli eventi enroll + /// + public MessagePipe EnrollMessPipe { get; set; } = null!; + public async Task ReleaseDelete(ReleaseModel rec2del) { bool fatto = dbControllerNext.ReleaseDelete(rec2del); @@ -993,10 +1188,29 @@ namespace LiMan.UI.Data stopWatch.Stop(); TimeSpan ts = stopWatch.Elapsed; Log.Trace($"Effettuata lettura da DB per ReleaseGetByApp | {CodApp} | {ts.TotalMilliseconds} ms"); - return dbResult; + return dbResult; #endif } + /// + /// Elenco Release dato Applicativo + versione minima + /// + /// Codice Applicazione + /// Versione minima richiesta + /// + public async Task> ReleaseGetByAppVers(string CodApp, string VersMin) + { + await Task.Delay(1); + List dbResult = new List(); + Stopwatch stopWatch = new Stopwatch(); + stopWatch.Start(); + dbResult = dbControllerNext.ReleaseGetByAppVers(CodApp, VersMin); + stopWatch.Stop(); + TimeSpan ts = stopWatch.Elapsed; + Log.Trace($"Effettuata lettura da DB per ReleaseGetByAppVers | {CodApp} | vers >= {VersMin} | {ts.TotalMilliseconds} ms"); + return dbResult; + } + /// /// Ultima Release dato Applicativo VALIDA (= rilasciata) /// @@ -1029,25 +1243,6 @@ namespace LiMan.UI.Data return answ; } - /// - /// Elenco Release dato Applicativo + versione minima - /// - /// Codice Applicazione - /// Versione minima richiesta - /// - public async Task> ReleaseGetByAppVers(string CodApp, string VersMin) - { - await Task.Delay(1); - List dbResult = new List(); - Stopwatch stopWatch = new Stopwatch(); - stopWatch.Start(); - dbResult = dbControllerNext.ReleaseGetByAppVers(CodApp, VersMin); - stopWatch.Stop(); - TimeSpan ts = stopWatch.Elapsed; - Log.Trace($"Effettuata lettura da DB per ReleaseGetByAppVers | {CodApp} | vers >= {VersMin} | {ts.TotalMilliseconds} ms"); - return dbResult; - } - /// /// Upsert record Release applicazione /// @@ -1233,8 +1428,6 @@ namespace LiMan.UI.Data return answ; } - private Dictionary UserClaimsLUT = new Dictionary(); - #endregion Public Methods #region Internal Methods @@ -1374,6 +1567,8 @@ namespace LiMan.UI.Data /// private int chSliExp = 60 * 1; + private Dictionary UserClaimsLUT = new Dictionary(); + #endregion Private Fields #region Private Methods diff --git a/LiMan.UI/LiMan.UI.csproj b/LiMan.UI/LiMan.UI.csproj index 0307c28..6e52949 100644 --- a/LiMan.UI/LiMan.UI.csproj +++ b/LiMan.UI/LiMan.UI.csproj @@ -2,7 +2,7 @@ net6.0 - 1.1.2410.1618 + 1.1.2501.0411 LiMan.UI LiMan.UI diff --git a/LiMan.UI/Pages/AppEnroll.razor b/LiMan.UI/Pages/AppEnroll.razor new file mode 100644 index 0000000..9ba6417 --- /dev/null +++ b/LiMan.UI/Pages/AppEnroll.razor @@ -0,0 +1,7 @@ +@page "/AppEnroll" +@attribute [Authorize] + + +
      + +
      \ No newline at end of file diff --git a/LiMan.UI/Pages/AppEnroll.razor.cs b/LiMan.UI/Pages/AppEnroll.razor.cs new file mode 100644 index 0000000..065c54f --- /dev/null +++ b/LiMan.UI/Pages/AppEnroll.razor.cs @@ -0,0 +1,7 @@ +namespace LiMan.UI.Pages +{ + public partial class AppEnroll + { + + } +} \ No newline at end of file diff --git a/LiMan.UI/Pages/LicenseManager.razor.cs b/LiMan.UI/Pages/LicenseManager.razor.cs index 8dd66f8..c3a8eee 100644 --- a/LiMan.UI/Pages/LicenseManager.razor.cs +++ b/LiMan.UI/Pages/LicenseManager.razor.cs @@ -79,7 +79,7 @@ namespace LiMan.UI.Pages { isLoading = true; await Task.Delay(1); - //AllRecords = null; + //SearchRecords = null; SearchRecords = await DataService.LicenzeGLSGetAll(); totalCount = SearchRecords.Count(); ListRecords = SearchRecords.Skip(numRecord * (currPage - 1)).Take(numRecord).ToList(); diff --git a/LiMan.UI/Resources/ChangeLog.html b/LiMan.UI/Resources/ChangeLog.html index 17c291a..7c4ac29 100644 --- a/LiMan.UI/Resources/ChangeLog.html +++ b/LiMan.UI/Resources/ChangeLog.html @@ -1,6 +1,6 @@ License Manager -

      Versione: 1.1.2410.1618

      +

      Versione: 1.1.2501.0411


      Note di rilascio:
      • diff --git a/LiMan.UI/Resources/VersNum.txt b/LiMan.UI/Resources/VersNum.txt index 17444b0..19632ec 100644 --- a/LiMan.UI/Resources/VersNum.txt +++ b/LiMan.UI/Resources/VersNum.txt @@ -1 +1 @@ -1.1.2410.1618 +1.1.2501.0411 diff --git a/LiMan.UI/Resources/manifest.xml b/LiMan.UI/Resources/manifest.xml index 332a1f0..4e6a2ec 100644 --- a/LiMan.UI/Resources/manifest.xml +++ b/LiMan.UI/Resources/manifest.xml @@ -1,6 +1,6 @@ - 1.1.2410.1618 + 1.1.2501.0411 https://nexus.steamware.net/repository/SWS/LiMan/stable/LAST/LiMan.UI.zip https://nexus.steamware.net/repository/SWS/LiMan/stable/LAST/ChangeLog.html false diff --git a/LiMan.UI/Shared/NavMenu.razor b/LiMan.UI/Shared/NavMenu.razor index 335419c..3a2bcc3 100644 --- a/LiMan.UI/Shared/NavMenu.razor +++ b/LiMan.UI/Shared/NavMenu.razor @@ -19,6 +19,11 @@
      • +