diff --git a/devdocs/todo.txt b/devdocs/todo.txt index 01b0654f..f8318eec 100644 --- a/devdocs/todo.txt +++ b/devdocs/todo.txt @@ -44,8 +44,6 @@ TODO: restrict server so randos can't login since the client now has all the log not sure how to do that and still support phone via cellular network or other people's wifi from logging in Firewall settings I guess of some kind or maybe require a manual edit to the password, like add a 1 to the end of all of them or something? -todo: Notification look for and implement //TODO: notify OPSNOTIFY - When notification system is in place todo: OPS notification created for failed jobs also maybe direct immediate email bypassing generator? diff --git a/server/AyaNova/Controllers/AttachmentController.cs b/server/AyaNova/Controllers/AttachmentController.cs index 4d4c636e..dbb3bc59 100644 --- a/server/AyaNova/Controllers/AttachmentController.cs +++ b/server/AyaNova/Controllers/AttachmentController.cs @@ -576,13 +576,12 @@ namespace AyaNova.Api.Controllers var filePath = FileUtil.GetPermanentAttachmentFilePath(dbObject.StoredFileName); if (!System.IO.File.Exists(filePath)) { - //TODO: notify OPSNOTIFY - //TODO: notify this should trigger some kind of notification to the ops people - //and a red light on the dashboard + //TODO: this should reset the validity var errText = $"Physical file {dbObject.StoredFileName} not found despite attachment record, this file is missing"; log.LogError(errText); + await NotifyEventProcessor.AddOpsProblemEvent($"File attachment issue: {errText}"); return NotFound(new ApiErrorResponse(ApiErrorCode.NOT_FOUND, null, errText)); } diff --git a/server/AyaNova/biz/AttachmentBiz.cs b/server/AyaNova/biz/AttachmentBiz.cs index b49e9539..9f0cdb8e 100644 --- a/server/AyaNova/biz/AttachmentBiz.cs +++ b/server/AyaNova/biz/AttachmentBiz.cs @@ -191,7 +191,7 @@ namespace AyaNova.Biz log.LogDebug(msg); await JobsBiz.LogJobAsync(job.GId, msg); - //TODO: notify OPSNOTIFY + await NotifyEventProcessor.AddOpsProblemEvent($"Attachments issue:{msg}"); var outList = ForeignFilesNotLikelyAttachmentsFoundInAttachmentsFolder.Take(25).ToList(); foreach (string s in outList) { diff --git a/server/AyaNova/biz/JobsBiz.cs b/server/AyaNova/biz/JobsBiz.cs index cf5e7323..abeac880 100644 --- a/server/AyaNova/biz/JobsBiz.cs +++ b/server/AyaNova/biz/JobsBiz.cs @@ -191,11 +191,11 @@ namespace AyaNova.Biz return; } - + //BACKUP await CoreJobBackup.DoWorkAsync(); - //NOTIFICATIONS + //NOTIFICATIONS await CoreJobNotify.DoWorkAsync(); await CoreNotificationSweeper.DoWorkAsync(); @@ -249,8 +249,9 @@ namespace AyaNova.Biz } catch (Exception ex) { - log.LogError(ex, "JobsBiz::ProcessJobsAsync unexpected error during processing"); - //TODO: notify OPSNOTIFY + var msg = "Server::ProcessJobsAsync unexpected error during processing"; + log.LogError(ex, msg); + await NotifyEventProcessor.AddOpsProblemEvent(msg, ex); } finally { diff --git a/server/AyaNova/biz/NotifyEventProcessor.cs b/server/AyaNova/biz/NotifyEventProcessor.cs index 2aa13154..8ff0bcfc 100644 --- a/server/AyaNova/biz/NotifyEventProcessor.cs +++ b/server/AyaNova/biz/NotifyEventProcessor.cs @@ -41,7 +41,7 @@ namespace AyaNova.Biz //This handles general notification events not requiring a decision or tied to an object that are basically just a immediate message to the user //e.g. ops problems, DefaultNotification, NotifyHealthCheck etc //optional user id to send directly to them - internal static async Task AddGeneralNotifyEvent(NotifyEventType eventType, string message, long userId = 0) + internal static async Task AddGeneralNotifyEvent(NotifyEventType eventType, string message, Exception except = null, long userId = 0) { log.LogTrace($"AddGeneralNotifyEvent processing: [type:{eventType}, userId:{userId}, message:{message}]"); #if (DEBUG) @@ -86,6 +86,11 @@ namespace AyaNova.Biz //check subscriptions for event and send accordingly to each user var subs = await ct.NotifySubscription.Where(z => z.EventType == eventType).ToListAsync(); + + //append exception message if not null + if (except != null) + message += $"\nException error: {ExceptionUtil.ExtractAllExceptionMessages(except)}"; + foreach (var sub in subs) { //note flag ~SERVER~ means to client to substitute "Server" translation key text instead diff --git a/server/AyaNova/biz/UserBiz.cs b/server/AyaNova/biz/UserBiz.cs index 9cb0f52e..9a55ce8c 100644 --- a/server/AyaNova/biz/UserBiz.cs +++ b/server/AyaNova/biz/UserBiz.cs @@ -53,9 +53,7 @@ namespace AyaNova.Biz { //Algorithm: For deactivation favor subcontractors first over servicetechs, favor ones that have no login records and finally favor by oldest last login - //theory is to catch the least likely to be currently active techs - - //TODO: notify OPSNOTIFY Notify about each and every tech that is set inactive to bizadmin users + //theory is to catch the least likely to be currently active techs var NoLoginTechList = await ct.User.Where(z => z.Active == true && z.LastLogin == null && ( z.UserType == UserType.Service || @@ -65,7 +63,9 @@ namespace AyaNova.Biz foreach (User u in NoLoginTechList) { u.Active = false; - _log.LogInformation($"User {u.Name} with no prior login automatically set to inactive to free up license"); + var msg = $"User {u.Name} with no prior login automatically set to inactive to free up downgraded license"; + await NotifyEventProcessor.AddOpsProblemEvent(msg); + _log.LogInformation(msg); } await ct.SaveChangesAsync(); @@ -89,7 +89,8 @@ namespace AyaNova.Biz User dbObject = ct.User.FirstOrDefault(z => z.Id == 1); dbObject.Password = Hasher.hash(dbObject.Salt, ServerBootConfig.AYANOVA_SET_SUPERUSER_PW); ct.SaveChanges(); - //TODO: notify OPSNOTIFY + + NotifyEventProcessor.AddOpsProblemEvent("AYANOVA_SET_SUPERUSER_PW setting was used at most recent server boot to reset SuperUser account password").Wait(); } } diff --git a/server/AyaNova/generator/CoreJobBackup.cs b/server/AyaNova/generator/CoreJobBackup.cs index 38710809..38eda510 100644 --- a/server/AyaNova/generator/CoreJobBackup.cs +++ b/server/AyaNova/generator/CoreJobBackup.cs @@ -43,13 +43,17 @@ namespace AyaNova.Biz } } AyaNova.Api.ControllerHelpers.ApiServerState apiServerState = null; + string BackupStatusNotification = string.Empty; try { BackupIsRunning = true; + //LOCK DOWN SERVER apiServerState = (AyaNova.Api.ControllerHelpers.ApiServerState)ServiceProviderProvider.Provider.GetService(typeof(AyaNova.Api.ControllerHelpers.ApiServerState)); apiServerState.SetClosed("BACKUP RUNNING"); - await JobsBiz.LogJobAsync(Guid.Empty, $"Starting backup job {(OnDemand ? "manual / on demand" : "scheduled") } "); + var jobstartmessage = $"Starting backup job {(OnDemand ? "manual / on demand" : "scheduled") } "; + await JobsBiz.LogJobAsync(Guid.Empty, jobstartmessage); + BackupStatusNotification = jobstartmessage + "\n"; log.LogDebug("Backup starting"); var DemandFileNamePrepend = OnDemand ? "manual-" : string.Empty; //************* @@ -74,13 +78,16 @@ namespace AyaNova.Biz var Result = RunProgram.Run(BackupUtilityCommand, Arguments, log, MAXIMUM_MS_ALLOWED_FOR_PROCESSING); if (string.IsNullOrWhiteSpace(Result)) { - log.LogDebug("BACKUP SUCCESSFUL (NO ERROR)"); + var ms = "Backup of database completed OK"; + log.LogDebug(ms); + BackupStatusNotification += (ms + "\n"); } else { - await JobsBiz.LogJobAsync(Guid.Empty, $"Error during data backup \"{Result}\""); + var msg = $"Error during data backup \"{Result}\""; + await JobsBiz.LogJobAsync(Guid.Empty, msg); log.LogError($"BACKUP ERROR: {Result}"); - //TODO: notify OPSNOTIFY + await NotifyEventProcessor.AddGeneralNotifyEvent(NotifyEventType.BackupStatus, msg); } //DO FILE BACKUP IF ATTACHMENTS BACKED UP @@ -88,6 +95,10 @@ namespace AyaNova.Biz { await JobsBiz.LogJobAsync(Guid.Empty, $"Attachments backup starting"); FileUtil.BackupAttachments(DemandFileNamePrepend); + var ms = "Backup of file attachments completed OK"; + log.LogDebug(ms); + BackupStatusNotification += (ms + "\n"); + } //PRUNE DATA BACKUP SETS NOT KEPT @@ -95,7 +106,8 @@ namespace AyaNova.Biz FileUtil.DatabaseBackupCleanUp(ServerGlobalOpsSettingsCache.Backup.BackupSetsToKeep); //v.next - COPY TO ONLINE STORAGE - //*************** + //*************** + log.LogDebug("Backup completed"); } catch (Exception ex) @@ -103,7 +115,7 @@ namespace AyaNova.Biz await JobsBiz.LogJobAsync(Guid.Empty, "Backup failed with errors:"); await JobsBiz.LogJobAsync(Guid.Empty, ExceptionUtil.ExtractAllExceptionMessages(ex)); log.LogError(ex, "Backup failed"); - //TODO: notify OPSNOTIFY + await NotifyEventProcessor.AddGeneralNotifyEvent(NotifyEventType.BackupStatus, "Backup failed", ex); throw ex; } finally @@ -114,6 +126,7 @@ namespace AyaNova.Biz apiServerState.ResumePriorState(); BackupIsRunning = false; await JobsBiz.LogJobAsync(Guid.Empty, $"Backup - fully complete, server re-opened"); + await NotifyEventProcessor.AddGeneralNotifyEvent(NotifyEventType.BackupStatus, $"Backup completed server re-opened\nStatus:{BackupStatusNotification}"); } } diff --git a/server/AyaNova/generator/CoreJobNotify.cs b/server/AyaNova/generator/CoreJobNotify.cs index 072877db..c83d1c18 100644 --- a/server/AyaNova/generator/CoreJobNotify.cs +++ b/server/AyaNova/generator/CoreJobNotify.cs @@ -3,16 +3,6 @@ using System.Threading.Tasks; using Microsoft.EntityFrameworkCore; using Microsoft.Extensions.Logging; using AyaNova.Models; -using System; -using System.Linq; -using System.Globalization; -using System.Text; -using System.Collections.Generic; -using System.Threading.Tasks; -using Microsoft.Extensions.Logging; -using Microsoft.EntityFrameworkCore; -using AyaNova.Util; -using AyaNova.Models; namespace AyaNova.Biz {