using Microsoft.Extensions.Configuration; using Microsoft.Extensions.Logging; using MP.AppAuth.Controllers; using MP.AppAuth.Models; using Newtonsoft.Json; using NLog; using StackExchange.Redis; using System; using System.Collections.Generic; using System.Diagnostics; using System.Linq; using System.Threading.Tasks; namespace MP.AppAuth.Services { public class AppAuthService : IDisposable { #region Public Fields // diritti (cablato) public const string RoleSuperAdmin = "MoonPro_SuperAdmin"; public static AppAuthController dbController; public static MPController MpDbController; public static AppUserController userController; #endregion Public Fields #region Private Fields // gestione key Redis private const string redisBaseAddr = "MP:LAND"; private const string rKeyDirittiUser = $"{redisBaseAddr}:DIR_USER"; private const string rKeyMacchine = $"{redisBaseAddr}:MACCHINE"; private const string rKeyPermUser = $"{redisBaseAddr}:PERM_USER"; private static IConfiguration _configuration; private static ILogger _logger; private static JsonSerializerSettings? JSSettings; private static Logger Log = LogManager.GetCurrentClassLogger(); private static string Modulo = ""; /// /// Durata cache lunga IN SECONDI /// private int cacheTtlLong = 60 * 5; /// /// Durata cache breve IN SECONDI /// private int cacheTtlShort = 60 * 1; /// /// Oggetto per connessione a REDIS /// private IConnectionMultiplexer redisConn; //ISubscriber sub = redis.GetSubscriber(); /// /// Oggetto DB redis da impiegare x chiamate R/W /// private StackExchange.Redis.IDatabase redisDb = null!; private Random rnd = new Random(); private Dictionary Vocabolario = new Dictionary(); #endregion Private Fields #region Public Constructors public AppAuthService(IConfiguration configuration, ILogger logger, IConnectionMultiplexer redisConnMult) { _logger = logger; _configuration = configuration; // cod app CodApp = _configuration.GetValue("SpecialConf:CodApp"); Modulo = _configuration.GetValue("ServerConf:Modulo"); // Conf cache redisConn = redisConnMult; redisDb = this.redisConn.GetDatabase(); // json serializer... FIX errore loop circolare https://www.ryadel.com/en/jsonserializationexception-self-referencing-loop-detected-error-fix-entity-framework-asp-net-core/ JSSettings = new JsonSerializerSettings() { ReferenceLoopHandling = ReferenceLoopHandling.Ignore }; // conf DB string connStr = _configuration.GetConnectionString("MP.Land.Auth"); if (string.IsNullOrEmpty(connStr)) { _logger.LogError("ConnString empty!"); } else { dbController = new AppAuthController(configuration); MpDbController = new MPController(configuration); userController = new AppUserController(configuration); _logger.LogInformation("DbController OK"); } } #endregion Public Constructors #region Private Properties private string CodApp { get; set; } = ""; /// /// Durata cache lunga (+ perturbazione percentuale +/-10%) /// private TimeSpan FastCache { get => TimeSpan.FromSeconds(cacheTtlShort * rnd.Next(900, 1100) / 1000); } /// /// Durata cache lunga (+ perturbazione percentuale +/-10%) /// private TimeSpan LongCache { get => TimeSpan.FromSeconds(cacheTtlLong * rnd.Next(900, 1100) / 1000); } /// /// Durata cache lunga (+ perturbazione percentuale +/-10%) /// private TimeSpan UltraLongCache { get => TimeSpan.FromSeconds(cacheTtlLong * 10 * rnd.Next(900, 1100) / 1000); } #endregion Private Properties #region Public Methods /// /// Lista Causali Scarto /// /// public async Task> AnagCauSca() { List dbResult = new List(); Stopwatch stopWatch = new Stopwatch(); stopWatch.Start(); dbResult = MpDbController.AnagCauSca(); stopWatch.Stop(); TimeSpan ts = stopWatch.Elapsed; Log.Trace($"Effettuata lettura da DB per AnagCauSca: {ts.TotalMilliseconds} ms"); return await Task.FromResult(dbResult); } /// /// Lista AnagClassiTempo /// /// public async Task> AnagClassiTempo() { List dbResult = new List(); Stopwatch stopWatch = new Stopwatch(); stopWatch.Start(); dbResult = MpDbController.AnagClassiTempo(); stopWatch.Stop(); TimeSpan ts = stopWatch.Elapsed; Log.Trace($"Effettuata lettura da DB per AnagClassiTempo: {ts.TotalMilliseconds} ms"); return await Task.FromResult(dbResult); } /// /// Lista AnagEventi /// /// public async Task> AnagEventi() { List dbResult = new List(); Stopwatch stopWatch = new Stopwatch(); stopWatch.Start(); dbResult = MpDbController.AnagEventi(); stopWatch.Stop(); TimeSpan ts = stopWatch.Elapsed; Log.Trace($"Effettuata lettura da DB per AnagEventi: {ts.TotalMilliseconds} ms"); return await Task.FromResult(dbResult); } /// /// Lista Gruppi /// /// public async Task> AnagGruppiAll() { List dbResult = new List(); Stopwatch stopWatch = new Stopwatch(); stopWatch.Start(); dbResult = dbController.AnagGruppiGetAll(); stopWatch.Stop(); TimeSpan ts = stopWatch.Elapsed; Log.Trace($"Effettuata lettura da DB per AnagGruppiAll: {ts.TotalMilliseconds} ms"); return await Task.FromResult(dbResult); } public async Task> AnagGruppiFilt(string codTipo) { List dbResult = new List(); Stopwatch stopWatch = new Stopwatch(); stopWatch.Start(); dbResult = dbController.AnagGruppiFilt(codTipo); stopWatch.Stop(); TimeSpan ts = stopWatch.Elapsed; Log.Trace($"Effettuata lettura da DB per AnagGruppiFilt: {ts.TotalMilliseconds} ms"); return await Task.FromResult(dbResult); } /// /// Lista AnagEventi /// /// public async Task> AnagIngressi() { List dbResult = new List(); Stopwatch stopWatch = new Stopwatch(); stopWatch.Start(); dbResult = MpDbController.AnagIngressi(); stopWatch.Stop(); TimeSpan ts = stopWatch.Elapsed; Log.Trace($"Effettuata lettura da DB per AnagIngressi: {ts.TotalMilliseconds} ms"); return await Task.FromResult(dbResult); } public async Task AnagKeyValAdd(AnagKeyValueModel currRec) { bool answ = false; Stopwatch stopWatch = new Stopwatch(); stopWatch.Start(); answ = MpDbController.AnagKeyValuesUpsert(currRec); stopWatch.Stop(); TimeSpan ts = stopWatch.Elapsed; Log.Trace($"AnagKeyValAdd | Aggiunto rec | NomeVar: {currRec.NomeVar} | durata: {ts.TotalMilliseconds} ms"); return await Task.FromResult(answ); } public async Task AnagKeyValDelete(string NomeVar) { bool answ = false; Stopwatch stopWatch = new Stopwatch(); stopWatch.Start(); answ = MpDbController.AnagKeyValuesDelete(NomeVar); stopWatch.Stop(); TimeSpan ts = stopWatch.Elapsed; Log.Trace($"AnagKeyValDelete | Effettuata cancellazione | NomeVar: {NomeVar} | durata: {ts.TotalMilliseconds} ms"); return await Task.FromResult(answ); } public async Task> AnagKeyValList() { List dbResult = new List(); Stopwatch stopWatch = new Stopwatch(); stopWatch.Start(); dbResult = MpDbController.AnagKeyValuesGetAll(); stopWatch.Stop(); TimeSpan ts = stopWatch.Elapsed; Log.Trace($"Effettuata lettura da DB per AnagKeyValList: {ts.TotalMilliseconds} ms"); return await Task.FromResult(dbResult); } public async Task AnagKeyValUpd(AnagKeyValueModel currRec) { bool answ = false; Stopwatch stopWatch = new Stopwatch(); stopWatch.Start(); answ = MpDbController.AnagKeyValuesUpsert(currRec); stopWatch.Stop(); TimeSpan ts = stopWatch.Elapsed; Log.Trace($"AnagKeyValUpd | Effettuata modifica | NomeVar: {currRec.NomeVar} | durata: {ts.TotalMilliseconds} ms"); return await Task.FromResult(answ); } /// /// Lista AnagEventi /// /// public async Task> AnagMicroStati() { List dbResult = new List(); Stopwatch stopWatch = new Stopwatch(); stopWatch.Start(); dbResult = MpDbController.AnagMicroStati(); stopWatch.Stop(); TimeSpan ts = stopWatch.Elapsed; Log.Trace($"Effettuata lettura da DB per AnagMicroStati: {ts.TotalMilliseconds} ms"); return await Task.FromResult(dbResult); } public async Task> AnagOperByGroupList(string codGruppo, string searchVal) { List dbResult = new List(); Stopwatch stopWatch = new Stopwatch(); stopWatch.Start(); var rawData = dbController .AnagOpByGruppoGetFilt(codGruppo, searchVal); dbResult = rawData .GroupBy(user => user.MatrOpr) .Select(grp => grp.First()) .ToList(); stopWatch.Stop(); TimeSpan ts = stopWatch.Elapsed; Log.Trace($"Effettuata lettura da DB per AnagOperByGroupList: {ts.TotalMilliseconds} ms"); return await Task.FromResult(dbResult); } public async Task> AnagOperList(string searchVal) { List dbResult = new List(); Stopwatch stopWatch = new Stopwatch(); stopWatch.Start(); dbResult = dbController.AnagOpGetAll(searchVal); stopWatch.Stop(); TimeSpan ts = stopWatch.Elapsed; Log.Trace($"Effettuata lettura da DB per AnagOperList: {ts.TotalMilliseconds} ms"); return await Task.FromResult(dbResult); } /// /// Lista AnagStati /// /// public async Task> AnagStati() { List dbResult = new List(); Stopwatch stopWatch = new Stopwatch(); stopWatch.Start(); dbResult = MpDbController.AnagStati(); stopWatch.Stop(); TimeSpan ts = stopWatch.Elapsed; Log.Trace($"Effettuata lettura da DB per AnagStati: {ts.TotalMilliseconds} ms"); return await Task.FromResult(dbResult); } public async Task ConfigAdd(ConfigModel currRec) { bool answ = false; Stopwatch stopWatch = new Stopwatch(); stopWatch.Start(); answ = MpDbController.ConfigUpsert(currRec); stopWatch.Stop(); TimeSpan ts = stopWatch.Elapsed; Log.Trace($"ConfigAdd | Aggiunto rec | Chiave: {currRec.Chiave} | durata: {ts.TotalMilliseconds} ms"); return await Task.FromResult(answ); } public async Task ConfigDelete(string Chiave) { bool answ = false; Stopwatch stopWatch = new Stopwatch(); stopWatch.Start(); answ = MpDbController.ConfigDelete(Chiave); stopWatch.Stop(); TimeSpan ts = stopWatch.Elapsed; Log.Trace($"ConfigDelete | Effettuata cancellazione | Chiave: {Chiave} | durata: {ts.TotalMilliseconds} ms"); return await Task.FromResult(answ); } public async Task> ConfigList() { List dbResult = new List(); Stopwatch stopWatch = new Stopwatch(); stopWatch.Start(); dbResult = MpDbController.ConfigGetAll(); stopWatch.Stop(); TimeSpan ts = stopWatch.Elapsed; Log.Trace($"Effettuata lettura da DB per ConfigList: {ts.TotalMilliseconds} ms"); return await Task.FromResult(dbResult); } public async Task ConfigUpd(ConfigModel currRec) { bool answ = false; Stopwatch stopWatch = new Stopwatch(); stopWatch.Start(); answ = MpDbController.ConfigUpsert(currRec); stopWatch.Stop(); TimeSpan ts = stopWatch.Elapsed; Log.Trace($"ConfigUpd | Effettuata modifica | Chiave: {currRec.Chiave} | durata: {ts.TotalMilliseconds} ms"); return await Task.FromResult(answ); } /// /// Elenco diritti dato utente /// /// /// public async Task> DirittiGetByUser(string UserName) { string source = "DB"; List? dbResult = new List(); string currKey = $"{rKeyDirittiUser}:{UserName}"; Stopwatch sw = new Stopwatch(); sw.Start(); string? rawData = await redisDb.StringGetAsync(currKey); if (!string.IsNullOrEmpty(rawData) && rawData.Length > 2) { source = "REDIS"; var tempResult = JsonConvert.DeserializeObject>(rawData); dbResult = tempResult ?? new List(); } else { // recupero diritti utente dbResult = userController.DirittiUtente(UserName, Modulo); rawData = JsonConvert.SerializeObject(dbResult, JSSettings); await redisDb.StringSetAsync(currKey, rawData, UltraLongCache); } if (dbResult == null) { dbResult = new List(); } sw.Stop(); Log.Debug($"DirittiGetByUser | {source} | {sw.ElapsedMilliseconds} ms"); return dbResult; } public void Dispose() { // Clear database controller if (MpDbController != null) { MpDbController.Dispose(); } } public async Task FlushRedisCache() { RedisValue pattern = new RedisValue($"{redisBaseAddr}:*"); bool answ = await ExecFlushRedisPattern(pattern); // reset in RAM Vocabolario = new Dictionary(); CheckVoc(); } /// /// Elenco ListValues /// /// public async Task> ListValues() { List dbResult = new List(); Stopwatch stopWatch = new Stopwatch(); stopWatch.Start(); dbResult = MpDbController.ListValues(); stopWatch.Stop(); TimeSpan ts = stopWatch.Elapsed; Log.Trace($"Effettuata lettura da DB per ListValues: {ts.TotalMilliseconds} ms"); return await Task.FromResult(dbResult); } /// /// Elenco Macchine gestite /// /// /// public List MacchineGetAll() { string source = "DB"; List? dbResult = new List(); string currKey = $"{rKeyMacchine}"; Stopwatch sw = new Stopwatch(); sw.Start(); string? rawData = redisDb.StringGet(currKey); if (!string.IsNullOrEmpty(rawData) && rawData.Length > 2) { source = "REDIS"; var tempResult = JsonConvert.DeserializeObject>(rawData); dbResult = tempResult ?? new List(); } else { // recupero diritti utente dbResult = MpDbController.MacchineGetAll(); rawData = JsonConvert.SerializeObject(dbResult, JSSettings); redisDb.StringSet(currKey, rawData, UltraLongCache); } if (dbResult == null) { dbResult = new List(); } sw.Stop(); Log.Debug($"MacchineGetAll | {source} | {sw.ElapsedMilliseconds} ms"); return dbResult; } /// /// Elenco permessi dato utente /// /// /// public async Task> PermessiGetByUser(string UserName) { string source = "DB"; List? dbResult = new List(); string currKey = $"{rKeyPermUser}:{UserName}"; 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 { // recupero diritti utente var userRightList = userController.DirittiUtente(UserName, Modulo); // proietto come funzioni... var ListFunc = userRightList.Select(x => x.Funzione).ToList(); // trasformo i permessi utente if (ListFunc == null) { ListFunc = new List(); } dbResult = dbController.PermessiGetByFunc(ListFunc); rawData = JsonConvert.SerializeObject(dbResult, JSSettings); await redisDb.StringSetAsync(currKey, rawData, UltraLongCache); } if (dbResult == null) { dbResult = new List(); } sw.Stop(); Log.Debug($"PermessiGetByUser | {source} | {sw.ElapsedMilliseconds} ms"); return dbResult; } public string Traduci(string lemma, string lingua = "IT") { string answ = $"__{lemma}__"; string keyReq = $"{lingua}#{lemma}"; CheckVoc(); // cerco in vocabolario... if (Vocabolario.ContainsKey(keyReq)) { answ = Vocabolario[keyReq]; } return answ; } public async Task> UpdManList() { List dbResult = new List(); Stopwatch stopWatch = new Stopwatch(); stopWatch.Start(); dbResult = dbController.UpdManGetAll(); stopWatch.Stop(); TimeSpan ts = stopWatch.Elapsed; Log.Trace($"Effettuata lettura da DB per UpdManList: {ts.TotalMilliseconds} ms"); return await Task.FromResult(dbResult); } public async Task VocabolarioAdd(VocabolarioModel currRec) { bool answ = false; Stopwatch stopWatch = new Stopwatch(); stopWatch.Start(); answ = MpDbController.VocabolarioUpsert(currRec); stopWatch.Stop(); TimeSpan ts = stopWatch.Elapsed; Log.Trace($"VocabolarioAdd | Aggiunto rec | lingua: {currRec.Lingua} | lemma: {currRec.Lemma} | durata: {ts.TotalMilliseconds} ms"); return await Task.FromResult(answ); } public async Task VocabolarioDelete(VocabolarioModel currRec) { bool answ = false; Stopwatch stopWatch = new Stopwatch(); stopWatch.Start(); answ = MpDbController.VocabolarioDelete(currRec); stopWatch.Stop(); TimeSpan ts = stopWatch.Elapsed; Log.Trace($"VocabolarioDelete | Effettuata cancellazione | lingua: {currRec.Lingua} | lemma: {currRec.Lemma} | durata: {ts.TotalMilliseconds} ms"); return await Task.FromResult(answ); } public async Task> VocabolarioList() { List dbResult = new List(); Stopwatch stopWatch = new Stopwatch(); stopWatch.Start(); dbResult = MpDbController.VocabolarioGetAll(); stopWatch.Stop(); TimeSpan ts = stopWatch.Elapsed; Log.Trace($"Effettuata lettura da DB per VocabolarioList: {ts.TotalMilliseconds} ms"); return await Task.FromResult(dbResult); } public async Task VocabolarioUpd(VocabolarioModel currRec) { bool answ = false; Stopwatch stopWatch = new Stopwatch(); stopWatch.Start(); answ = MpDbController.VocabolarioUpsert(currRec); stopWatch.Stop(); TimeSpan ts = stopWatch.Elapsed; Log.Trace($"VocabolarioUpd | Effettuata modifica | lingua: {currRec.Lingua} | lemma: {currRec.Lemma} | durata: {ts.TotalMilliseconds} ms"); return await Task.FromResult(answ); } #endregion Public Methods #region Protected Methods protected void CheckVoc() { if (Vocabolario.Count == 0) { string source = "DB"; string currKey = $"{redisBaseAddr}:MP:VOCAB"; Stopwatch sw = new Stopwatch(); sw.Start(); string? rawData = redisDb.StringGet(currKey); if (!string.IsNullOrEmpty(rawData) && rawData.Length > 2) { source = "REDIS"; var tempResult = JsonConvert.DeserializeObject>(rawData); Vocabolario = tempResult ?? new Dictionary(); } else { Vocabolario = dbController .VocabolarioGetAll() .ToDictionary(x => $"{x.Lingua}#{x.Lemma}", x => x.Traduzione); rawData = JsonConvert.SerializeObject(Vocabolario); redisDb.StringSet(currKey, rawData, UltraLongCache); } sw.Stop(); Log.Trace($"Rilettura Vocabolario | {source} | {sw.ElapsedMilliseconds} ms"); } } #endregion Protected Methods #region Private Methods /// /// Esegue flush memoria redis dato pat2Flush /// /// /// private async Task ExecFlushRedisPattern(RedisValue pat2Flush) { bool answ = false; var masterEndpoint = redisConn.GetEndPoints() .Where(ep => redisConn.GetServer(ep).IsConnected && !redisConn.GetServer(ep).IsReplica) .FirstOrDefault(); // sepattern è "*" elimino intero DB... if (masterEndpoint != null && (pat2Flush.Equals(new RedisValue("*")) || pat2Flush == RedisValue.Null)) { redisConn.GetServer(masterEndpoint).FlushDatabase(database: redisDb.Database); } else { var server = redisConn.GetServer(masterEndpoint); var keys = server.Keys(database: redisDb.Database, pattern: pat2Flush, pageSize: 1000); var deleteTasks = new List(); foreach (var key in keys) { deleteTasks.Add(redisDb.KeyDeleteAsync(key)); if (deleteTasks.Count >= 1000) { await Task.WhenAll(deleteTasks); deleteTasks.Clear(); } } if (deleteTasks.Count > 0) { await Task.WhenAll(deleteTasks); } } answ = true; return answ; } #endregion Private Methods } }