using EgwControlCenter.Core.DTO; using EgwControlCenter.Core.Models; using Newtonsoft.Json; using NLog; using RestSharp; using RestSharp.Serializers; using System; using System.Collections.Generic; using System.IO; using System.Linq; using System.Text; using System.Threading.Tasks; using System.Web; using System.Windows.Markup; namespace EgwControlCenter.Core { public class ReleaseChecker { #region Public Constructors /// /// Init classe ReleaseChecker /// /// /// /// public ReleaseChecker(string appDir, string dataDir, string confName) { InitObj(appDir, dataDir, confName); } /// /// Init classe ReleaseChecker /// /// /// public ReleaseChecker(string appDir, string dataDir) { InitObj(appDir, dataDir, CNameStd); } #endregion Public Constructors #region Public Properties /// /// Path file di conf check /// public string ConfPath { get => Path.Combine(DataDir, ConfName); } /// /// Path file di conf check /// public string ConfPathTemplate { get => Path.Combine(AppDir, ConfName); } /// /// Configurazione degli oggetti da monitorare /// public PatrolSettings CurrPatrolCont { get; set; } = new PatrolSettings(); /// /// Elenco degli oggetti stato dei programmi monitorati /// public List ListAppStatus { get; set; } = new List(); #endregion Public Properties #region Public Methods /// /// Esegue verifica remota attivazioni, e di conseguenza eventualmente attiva/elimina... /// /// public async Task CheckAttivazioni() { bool fatto = false; try { // verifico release da remoto aggiorno obj var activRec = await AttivazioneGetRemote(CurrPatrolCont.MainKey, CurrPatrolCont.CodImpiego); if (activRec != null && activRec.CodImpiego == CurrPatrolCont.CodImpiego && activRec.Chiave == CurrPatrolCont.AppKey) { // sono a posto! fatto = true; } else { // creo nuovo record attivazione... var actRes = await AttivazioneTrySet(CurrPatrolCont.MainKey, CurrPatrolCont.CodImpiego, CurrPatrolCont.AppKey); fatto = actRes != null && actRes.CodImpiego == CurrPatrolCont.CodImpiego && actRes.Chiave == CurrPatrolCont.AppKey; } if (fatto) { // salvo status... SaveConfig(); } } catch (Exception exc) { Log.Error($"Errore in CheckAttivazioni{Environment.NewLine}{exc}"); } return fatto; } /// /// Esegue verifica remota se ci siano release CRITICAL attive e le accoda immediatamente /// /// public async Task CheckCriticalReport() { bool setUpdated = false; try { // scarica il tracciato di TUTTE le releases critiche attive Dictionary listRemote = await GetRemoteCritical(); // primo check: confrontra con elenco salvato... foreach (var item in listRemote) { // se è stato aggionto if (!CriticalRelCurrent.ContainsKey(item.Key)) { var locStatus = ListStatus.Where(x => x.CodApp == item.Key).FirstOrDefault(); // --> verifico se faccia parte dell'elenco //if (locStatus!=null && locStatus.CodApp==item.Key) if (locStatus != null) { // inserisco in listRemote la rel critical trovata... List ListRem = new List(); ListRem.Add(item.Value); if (ListRem != null) { locStatus.ListRemote = ListRem; locStatus.LastUpdateRem = DateTime.Now; // indico checkOk a true setUpdated = true; } } } } // aggiorna set rel current CriticalRelCurrent = listRemote; // se ho fatto aggiornamenti locali salvo! if (setUpdated) { SaveStatus(); } } catch (Exception exc) { Log.Error($"Errore in CheckCriticalReport{Environment.NewLine}{exc}"); } return setUpdated; } /// /// Esegue verifica remota versioni x i sw tracciati /// /// public async Task CheckRemoteReleases() { bool fatto = false; try { // ciclo x ogni item da controllare foreach (var item in ListStatus) { if (item.CurrLocal != null) { // verifico release da remoto aggiorno obj var ListRem = await GetRemoteRel(item.CodApp, item.CurrLocal.VersNum); if (ListRem != null) { item.ListRemote = ListRem; item.LastUpdateRem = DateTime.Now; } } } // salvo status... SaveStatus(); fatto = true; } catch (Exception exc) { Log.Error($"Errore in CheckRemoteReleases{Environment.NewLine}{exc}"); } return fatto; } public bool HasUpdate() { bool answ = false; //se ho record locali cerco il + recente if (ListStatus != null && ListStatus.Count > 0) { foreach (var item in ListStatus) { answ = item.HasUpdate(); if (answ) { break; } } } return answ; } /// /// DataOra ultimo check locale /// public DateTime LastChecked() { DateTime answ = new DateTime(DateTime.Today.Year, 1, 1); //se ho record locali cerco il + recente if (ListStatus != null && ListStatus.Count > 0) { var primo = ListStatus .OrderByDescending(x => x.LastUpdateLoc) .FirstOrDefault(); if (primo != null) { answ = primo.LastUpdateLoc; } } return answ; } /// /// Effettua rilettura configurazione + init /// /// public bool ReloadConfData() { bool fatto = false; try { ReloadData(); fatto = true; } catch (Exception exc) { Log.Error($"Eccezione in ReloadConfData{Environment.NewLine}{exc}"); } return fatto; } /// /// Effettua reset configurazione sovrascrivendo da template /// /// public bool ResetConf() { bool fatto = false; try { File.Copy(ConfPathTemplate, ConfPath, true); ReloadData(); fatto = true; } catch (Exception exc) { Log.Error($"Eccezione in ResetConf{Environment.NewLine}{exc}"); } return fatto; } //Salvataggio configurazioni public void SaveConfig() { var rawData = JsonConvert.SerializeObject(CurrPatrolCont, Formatting.Indented); if (rawData != null && rawData.Length > 2) { File.WriteAllText(ConfPath, rawData); } } /// /// Aggiorna (se necessario) lo stato degli oggetti da monitorare /// /// /// public bool UpdateLocalStatus(bool doRefresh) { bool fatto = false; // se non forzato verifico file dello status salvato x necessità refresh if (!doRefresh) { doRefresh = CheckScaduto(); } // verifico se procedere... if (doRefresh) { // nuovo obj controlli List newListStatus = new List(); // ciclo x ogni item da controllare foreach (var item in CurrPatrolCont.TargetList) { // verifico contenuto secondo tipo if (item != null && item.IsEnabled) { switch (item.ApplicationType) { case CoreEnum.AppType.None: break; case CoreEnum.AppType.Cli: break; case CoreEnum.AppType.Machine: // processo la folder indicata e cerco tutte le macchine contenute if (Directory.Exists(item.BasePath)) { // recupero elenco sottodirectory = macchine var dirList = Directory.GetDirectories(item.BasePath); // ciclo e cerco i file mlde... foreach (var currDir in dirList) { // cerco se ci sia un file mlde... var fileFound = Directory.GetFiles(currDir, "*.mlde"); if (fileFound != null && fileFound.Count() > 0) { // prendo il primo... var tgtFile = fileFound.FirstOrDefault() ?? ""; var currRelDto = MldeGetRelease(tgtFile); if (currRelDto != null) { string codApp = Path.GetFileName(currDir); newListStatus.Add(new TargetStatus() { CodApp = codApp, CurrLocal = currRelDto, LastUpdateLoc = DateTime.Now }); } } } } break; case CoreEnum.AppType.WebApp: break; case CoreEnum.AppType.WinApp: break; default: break; } } // aggiungo oggetto TargetStatus } // aggiorno obj... ListStatus = newListStatus; // salvo status... SaveStatus(); fatto = true; } return fatto; } #endregion Public Methods #region Protected Properties /// /// Periodo minimo per autorefresh versioni (default 1 gg) /// NB: in lettura viene perturbato causalmente del 5%... /// protected TimeSpan AutoRefreshVeto { get; set; } = TimeSpan.FromMinutes(30); /// /// Elenco degli oggetti stato dei programmi monitorati /// protected List ListStatus { get; set; } = new List(); #endregion Protected Properties #region Private Fields /// /// Classe logger /// private static Logger Log = LogManager.GetCurrentClassLogger(); /// /// Conf client RestSharp standard: /// - base URI al sito /// - timeout 1 min /// private static RestClientOptions restOptStd = new RestClientOptions { Timeout = TimeSpan.FromSeconds(60), BaseUrl = new Uri(Const.apiUrl) }; /// /// Conf client RestSharp fast (usato x check remote): /// - base URI al sito /// - timeout 5 sec /// private static RestClientOptions restOptFast = new RestClientOptions { Timeout = TimeSpan.FromSeconds(5), BaseUrl = new Uri(Const.apiUrl) }; private Random rnd = new Random(); /// /// Indica se usare la chaive appKey x i metodi di recupero informazioni 8elenco versioni...) /// private bool useAppKey = true; #endregion Private Fields #region Private Properties private string AppDir { get; set; } = ""; private string CNameStd { get; set; } = "ConfPatrol.json"; private string ConfName { get; set; } = ""; private Dictionary CriticalRelCurrent { get; set; } = new Dictionary(); private string DataDir { get; set; } = ""; private string LSName { get; set; } = "LastStatus.json"; /// /// Path file salvataggio status /// private string StatusPath { get => Path.Combine(DataDir, LSName); //get => Path.Combine(ApplicationDeployment.CurrentDeployment.DataDirectory, LSName); } #endregion Private Properties #region Private Methods /// /// Verifica recuperandol 8se presente) una SubLicenza applicativa /// /// /// /// private static async Task AttivazioneGetRemote(string MasterKey, string CodImp) { AttivazioneDTO answ = new AttivazioneDTO(); try { // client chiamate rest var client = new RestClient(restOptStd); // Chiamo il metodo! string MKeyEnc = HttpUtility.UrlEncode(MasterKey); var checkRel = new RestRequest($"/api/attivazioni/verifica/?chiave={MKeyEnc}&codImpiego={CodImp}", Method.Get); // effettuo vera chiamata var currResp = await client.GetAsync(checkRel); if (currResp.StatusCode == System.Net.HttpStatusCode.OK && currResp.Content != null) { // salvo in redis contenuto serializzato string rawData = $"{currResp.Content}"; var currVal = JsonConvert.DeserializeObject(rawData); if (currVal != null) { answ = currVal; } } } catch (Exception exc) { Log.Error($"Eccezione in fase gestione REST services AttivazioneGetRemote{Environment.NewLine}{exc}"); } return answ; } /// /// Verifica recuperandol 8se presente) una SubLicenza applicativa /// /// /// /// /// private static async Task AttivazioneTrySet(string MasterKey, string CodImp, string AppKey) { AttivazioneDTO answ = new AttivazioneDTO(); try { // client chiamate rest var client = new RestClient(restOptStd); var paramDict = new Dictionary(); paramDict.Add(CodImp, AppKey); // preparo oggetto richiesta... SubLicenseRequestDTO reqDto = new SubLicenseRequestDTO() { MasterKey = MasterKey, LicType = CoreEnum.TipoLicenza.AppSubLic, ParamDict = paramDict }; // Chiamo il metodo! var actReq = new RestRequest($"/api/attivazioni", Method.Post); string payload = JsonConvert.SerializeObject(reqDto); actReq.AddJsonBody(payload); // effettuo vera chiamata var currResp = await client.PostAsync(actReq); if (currResp.StatusCode == System.Net.HttpStatusCode.OK && currResp.Content != null) { // mi restituisce elenco attivazioni var currList = JsonConvert.DeserializeObject>(currResp.Content); if (currList != null) { // se contiene anche la richiesta è ok... var recUpd = currList.FirstOrDefault(x => x.CodImpiego == CodImp && x.Chiave == AppKey); if (recUpd != null) { answ = recUpd; } } } } catch (Exception exc) { Log.Error($"Eccezione in fase gestione REST services AttivazioneTrySet{Environment.NewLine}{exc}"); } return answ; } /// /// Verifica se il check sia scaduto rispetto ad AutoRefreshVeto /// /// private bool CheckScaduto() { bool answ = false; DateTime adesso = DateTime.Now; // ciclo per ogni oggetto della lista... foreach (var item in ListStatus) { if (item.LastUpdateLoc.Add(AutoRefreshVeto).Add(TimeSpan.FromSeconds(rnd.Next(-60, 120))) < adesso) { answ = true; // esco! break; } } return answ; } /// /// Recupera elenco applicazioni con ultima release CRITICAL /// /// private async Task> GetRemoteCritical() { // in questo caso la richiesta è per la generica app UpdateManager... string CodApp = "UpdateManager"; Dictionary answ = new Dictionary(); try { // client chiamate rest var client = new RestClient(restOptStd); // provo cmq ad inizializzare la richiesta ReleaseReqDTO reqDto = new ReleaseReqDTO() { CodApp = CodApp, CodImp = CurrPatrolCont.CodImpiego, AppKey = CurrPatrolCont.AppKey }; // Chiamo il metodo! if (useAppKey && reqDto.IsValid) { var actReq = new RestRequest($"/api/release/getcritical", Method.Post); string payload = JsonConvert.SerializeObject(reqDto); actReq.AddJsonBody(payload); // effettuo vera chiamata var currResp = await client.PostAsync(actReq); if (currResp.StatusCode == System.Net.HttpStatusCode.OK && currResp.Content != null) { var currList = JsonConvert.DeserializeObject>(currResp.Content); if (currList != null) { answ = currList; } } else { Log.Error($"Errore in ricezione info REST GetRemoteCritical | StatusCode: {currResp.StatusCode} | content: {currResp.Content}"); } } } catch (Exception exc) { Log.Error($"Eccezione in fase gestione REST services GetRemoteCritical{Environment.NewLine}{exc}"); } return answ; } /// /// Recupera le rel remote data una applicazione e la versione di partenza di interesse /// /// /// /// private async Task> GetRemoteRel(string CodApp, string CurrVers) { List answ = new List(); try { // client chiamate rest var client = new RestClient(restOptStd); // provo cmq ad inizializzare la richiesta ReleaseReqDTO reqDto = new ReleaseReqDTO() { CodApp = CodApp, CodImp = CurrPatrolCont.CodImpiego, AppKey = CurrPatrolCont.AppKey, VersMin = CurrVers }; // Chiamo il metodo! if (useAppKey && reqDto.IsValid) { var actReq = new RestRequest($"/api/release/getfilt", Method.Post); string payload = JsonConvert.SerializeObject(reqDto); actReq.AddJsonBody(payload); // effettuo vera chiamata var currResp = await client.PostAsync(actReq); if (currResp.StatusCode == System.Net.HttpStatusCode.OK && currResp.Content != null) { var currList = JsonConvert.DeserializeObject>(currResp.Content); if (currList != null) { answ = currList; } } } else { var checkRel = new RestRequest($"/api/release/filt/{CodApp}?VersMin={CurrVers}", Method.Get); // effettuo vera chiamata var currResp = await client.GetAsync(checkRel); if (currResp.StatusCode == System.Net.HttpStatusCode.OK && currResp.Content != null) { var currList = JsonConvert.DeserializeObject>(currResp.Content); if (currList != null) { answ = currList; } } } } catch (Exception exc) { Log.Error($"Eccezione in fase gestione REST services GetRemoteRel{Environment.NewLine}{exc}"); } return answ; } /// /// Verifica se il server remoto sia in buono stato... /// /// public async Task CheckRemote() { // in questo caso la richiesta è per la generica app UpdateManager... string CodApp = "UpdateManager"; bool answ = false; try { // client chiamate rest var client = new RestClient(restOptFast); // provo cmq ad inizializzare la richiesta ReleaseReqDTO reqDto = new ReleaseReqDTO() { CodApp = CodApp, CodImp = CurrPatrolCont.CodImpiego, AppKey = CurrPatrolCont.AppKey }; // Chiamo il metodo! if (useAppKey && reqDto.IsValid) { var actReq = new RestRequest($"/api/health", Method.Get); // effettuo vera chiamata var currResp = await client.GetAsync(actReq); if (currResp.StatusCode == System.Net.HttpStatusCode.OK && currResp.Content != null) { var rawData = $"{currResp.Content}"; answ = rawData.Contains("OK", StringComparison.InvariantCultureIgnoreCase); } else { Log.Error($"Errore in ricezione info REST CheckRemote | StatusCode: {currResp.StatusCode} | content: {currResp.Content}"); } } } catch (Exception exc) { Log.Error($"Eccezione in fase gestione REST services CheckRemote{Environment.NewLine}{exc}"); } return answ; } /// /// init privato oggetti /// /// /// /// private void InitObj(string appDir, string dataDir, string confName) { AppDir = appDir; DataDir = dataDir; ConfName = confName; // imposto veto controlli secondo config verifico path... if (string.IsNullOrEmpty(AppDir)) { AppDir = AppDomain.CurrentDomain.BaseDirectory; } // copio il file di conf SE mancasse... if (!File.Exists(ConfPath)) { File.Copy(ConfPathTemplate, ConfPath); } ReloadData(); } /// /// Recupera oggetto releaseDTO da file indicato /// /// /// private ReleaseDTO? MldeGetRelease(string filePath) { ReleaseDTO? answ = null; if (File.Exists(filePath)) { string versNum = "0.0.0.0"; string versTxt = "0.0.0.0"; string codApp = ""; // leggo una riga alla volta e cerco le chaivi using (StreamReader reader = new StreamReader(filePath)) { string? line; bool stop = false; while ((line = reader.ReadLine()) != null && !stop) { if (line.StartsWith("PP_VER")) { // splitto e prendo secondo var splitTxt = line.Split("="); if (splitTxt.Length > 1) { versTxt = splitTxt[1].Trim().Replace("'", "").Trim(); } } else if (line.StartsWith("PP_NVER")) { // splitto e prendo secondo var splitTxt = line.Split("="); if (splitTxt.Length > 1) { versNum = splitTxt[1].Trim().Replace("'", "").Trim(); } } else if (line.StartsWith("MACH_NAME")) { // splitto e prendo secondo var splitTxt = line.Split("="); if (splitTxt.Length > 1) { codApp = splitTxt[1].Trim().Replace("'", "").Trim(); } } // se ho trovato tutto esco! stop = !string.IsNullOrEmpty(versNum) && !string.IsNullOrEmpty(versTxt) && !string.IsNullOrEmpty(codApp); } } // creo OBJ e lo restituisco... answ = new ReleaseDTO() { CodApp = codApp, ReleaseDate = DateTime.Now, VersNum = versNum, VersText = versTxt }; } return answ; } /// /// Rilegge i dati di configurazione e ultimo stato salvato /// private void ReloadData() { if (File.Exists(ConfPath)) { var rawData = File.ReadAllText(ConfPath); if (!string.IsNullOrEmpty(rawData)) { CurrPatrolCont = JsonConvert.DeserializeObject(rawData) ?? new PatrolSettings(); // imposto il veto dalla conf... AutoRefreshVeto = TimeSpan.FromMinutes(CurrPatrolCont.VetoCheckMinutes); } } // cerco se presente file status precedente e lo ricarico... if (File.Exists(StatusPath)) { var rawData = File.ReadAllText(StatusPath); if (!string.IsNullOrEmpty(rawData)) { ListStatus = JsonConvert.DeserializeObject>(rawData) ?? new List(); } } } /// /// Effettua salvataggio obj status /// private void SaveStatus() { var rawData = JsonConvert.SerializeObject(ListStatus, Formatting.Indented); if (rawData != null && rawData.Length > 2) { File.WriteAllText(StatusPath, rawData); } // aggiorno obj calcolato versioni... ListAppStatus = ListStatus //.Where(x => x.HasUpdate()) .Select(x => new VersStatusDTO { CodApp = x.CodApp, VersNumCurr = x.CurrLocal.VersNum, VersNumLast = x.CurrRemote.VersNum, RelTagsLast = x.CurrRemote.RelTags, HasUpdate = x.HasUpdate() }) .ToList(); } #endregion Private Methods } }