diff --git a/devdocs/specs/core-log-business.txt b/devdocs/specs/core-log-business.txt index 71156c5a..44c9d314 100644 --- a/devdocs/specs/core-log-business.txt +++ b/devdocs/specs/core-log-business.txt @@ -46,11 +46,18 @@ TEXTRA (text field to identify stuff that can't be retrieved from source object, DELETION -------- -The only way an entry is removed from the log is via deletion of the source object +Initially, the only way an entry is removed from the log is via deletion of the source object When a source object is deleted, the entire history for that object is removed from the log and a new entry is created showing the deletion and textual representation of the object - The record has the object type so no need to redundently store that, just the name: "1234" for workorder 1234 or "ABC Accounting" for the client name etc +PURGING (FUTURE) +---------------- +Items that might be most purgeable in any future purge scenario: +- Retrieved events +- File attachment download events +- fetch logfile + TODO: 1) Implement this log and routes to interact with it diff --git a/server/AyaNova/Controllers/AttachmentController.cs b/server/AyaNova/Controllers/AttachmentController.cs index b0c1874a..29e366f2 100644 --- a/server/AyaNova/Controllers/AttachmentController.cs +++ b/server/AyaNova/Controllers/AttachmentController.cs @@ -59,7 +59,7 @@ namespace AyaNova.Api.Controllers - + //TODO: Centralize this code somewhere else, it's going to be needed for backup as well //consider the 1 hour thing, is this legit depending on client? @@ -224,18 +224,23 @@ namespace AyaNova.Api.Controllers return BadRequest(new ApiErrorResponse(ApiErrorCode.VALIDATION_INVALID_VALUE, null, errorMessage)); } + long UserId = UserIdFromContext.Id(HttpContext.Items); //We have our files and a confirmed AyObject, ready to attach and save permanently if (uploadFormData.UploadedFiles.Count > 0) { foreach (UploadedFileInfo a in uploadFormData.UploadedFiles) { - var v = FileUtil.storeFileAttachment(a.InitialUploadedPathName, a.MimeType, a.OriginalFileName, UserIdFromContext.Id(HttpContext.Items), attachToObject, ct); + var v = FileUtil.storeFileAttachment(a.InitialUploadedPathName, a.MimeType, a.OriginalFileName, UserId, attachToObject, ct); returnList.Add(new NameIdItem() { Name = v.DisplayFileName, Id = v.Id }); + + //Log + EventLogProcessor.AddEntry(new Event(UserId, attachToObject.ObjectId, attachToObject.ObjectType, AyaEvent.AttachmentCreate, v.DisplayFileName), ct); + } } } @@ -289,6 +294,7 @@ namespace AyaNova.Api.Controllers return NotFound(new ApiErrorResponse(ApiErrorCode.NOT_FOUND)); } + long UserId = UserIdFromContext.Id(HttpContext.Items); if (!Authorized.IsAuthorizedToDelete(HttpContext.Items, dbObj.AttachToObjectType, dbObj.OwnerId)) { @@ -299,6 +305,9 @@ namespace AyaNova.Api.Controllers //this handles removing the file if there are no refs left and also the db record for the attachment FileUtil.deleteFileAttachment(dbObj, ct); + //Log + EventLogProcessor.AddEntry(new Event(UserId, dbObj.AttachToObjectId, dbObj.AttachToObjectType, AyaEvent.AttachmentDelete, dbObj.DisplayFileName), ct); + return NoContent(); } @@ -339,8 +348,8 @@ namespace AyaNova.Api.Controllers } //Make sure the token provided is for the current user - long lAuthenticatedUserId = UserIdFromContext.Id(HttpContext.Items); - if (lAuthenticatedUserId != dlkeyUser.Id) + long UserId = UserIdFromContext.Id(HttpContext.Items); + if (UserId != dlkeyUser.Id) { return BadRequest(new ApiErrorResponse(ApiErrorCode.NOT_AUTHORIZED, "dlkey", "Download token not valid")); } @@ -375,6 +384,9 @@ namespace AyaNova.Api.Controllers return NotFound(new ApiErrorResponse(ApiErrorCode.NOT_FOUND, null, $"Physical file {dbObj.StoredFileName} not found despite attachment record, this file is missing")); } + //Log + EventLogProcessor.AddEntry(new Event(UserId, dbObj.AttachToObjectId, dbObj.AttachToObjectType, AyaEvent.AttachmentDownload, dbObj.DisplayFileName), ct); + return PhysicalFile(filePath, mimetype, dbObj.DisplayFileName); } diff --git a/server/AyaNova/Controllers/LicenseController.cs b/server/AyaNova/Controllers/LicenseController.cs index eb0b1e57..a309fe49 100644 --- a/server/AyaNova/Controllers/LicenseController.cs +++ b/server/AyaNova/Controllers/LicenseController.cs @@ -126,6 +126,8 @@ namespace AyaNova.Api.Controllers } var ret = AyaNova.Core.License.LicenseInfoAsJson; + //Log + EventLogProcessor.AddEntry(new Event(UserIdFromContext.Id(HttpContext.Items), 0, AyaType.License, AyaEvent.LicenseFetch), ct); return Ok(new ApiOkResponse(ret)); } @@ -176,6 +178,9 @@ namespace AyaNova.Api.Controllers //Send the request to RockFish here (or at least start the job to do it in which case return Accepted instead of no content and update comment above) var ret = Core.License.RequestTrial(requestData.EmailAddress, requestData.RegisteredTo, log); + //Log + EventLogProcessor.AddEntry(new Event(UserIdFromContext.Id(HttpContext.Items), 0, AyaType.License, AyaEvent.LicenseTrialRequest), ct); + return Ok(new ApiOkResponse(ret)); } diff --git a/server/AyaNova/Controllers/LocaleController.cs b/server/AyaNova/Controllers/LocaleController.cs index f99c1c4c..6550a15b 100644 --- a/server/AyaNova/Controllers/LocaleController.cs +++ b/server/AyaNova/Controllers/LocaleController.cs @@ -161,6 +161,10 @@ namespace AyaNova.Api.Controllers { //save and success return await ct.SaveChangesAsync(); + + //Log + EventLogProcessor.AddEntry(new Event(biz.userId, o.Id, AyaType.Locale, AyaEvent.Created), ct); + return CreatedAtAction("GetLocale", new { id = o.Id }, new ApiCreatedResponse(o)); } } @@ -236,6 +240,9 @@ namespace AyaNova.Api.Controllers } } + //Log + EventLogProcessor.AddEntry(new Event(biz.userId, oDbParent.Id, AyaType.Locale, AyaEvent.Modified), ct); + return Ok(new ApiOkResponse(new { ConcurrencyToken = oFromDb.ConcurrencyToken })); } @@ -269,7 +276,7 @@ namespace AyaNova.Api.Controllers return NotFound(new ApiErrorResponse(ApiErrorCode.NOT_FOUND)); } - + if (!Authorized.IsAuthorizedToModify(HttpContext.Items, AyaType.Locale, oFromDb.OwnerId)) { return StatusCode(401, new ApiNotAuthorizedResponse()); @@ -302,6 +309,9 @@ namespace AyaNova.Api.Controllers } } + //Log + EventLogProcessor.AddEntry(new Event(biz.userId, oFromDb.Id, AyaType.Locale, AyaEvent.Modified), ct); + return Ok(new ApiOkResponse(new { ConcurrencyToken = oFromDb.ConcurrencyToken })); } @@ -355,6 +365,9 @@ namespace AyaNova.Api.Controllers //Delete children / attached objects // biz.DeleteChildren(dbObj); + //Log + EventLogProcessor.DeleteObject(biz.userId, AyaType.Locale, dbObj.Id, dbObj.Name, ct); + return NoContent(); } diff --git a/server/AyaNova/Controllers/LogFilesController.cs b/server/AyaNova/Controllers/LogFilesController.cs index 4af5099b..c70182e4 100644 --- a/server/AyaNova/Controllers/LogFilesController.cs +++ b/server/AyaNova/Controllers/LogFilesController.cs @@ -80,19 +80,9 @@ namespace AyaNova.Api.Controllers return NotFound(new ApiErrorResponse(ApiErrorCode.NOT_FOUND)); } - // //read it and stream it back in a json object - - - // Newtonsoft.Json.Linq.JObject o = Newtonsoft.Json.Linq.JObject.FromObject(new - // { - // log = new - // { - // name = logname, - // log = System.IO.File.ReadAllText(logFilePath) - // } - // }); - - // return Ok(new ApiOkResponse(o)); + + //Log + EventLogProcessor.AddEntry(new Event(UserIdFromContext.Id(HttpContext.Items), 0, AyaType.LogFile, AyaEvent.Retrieved,logname), ct); return Content(System.IO.File.ReadAllText(logFilePath)); diff --git a/server/AyaNova/Controllers/MetricsController.cs b/server/AyaNova/Controllers/MetricsController.cs index 2da0f673..43d2c5f7 100644 --- a/server/AyaNova/Controllers/MetricsController.cs +++ b/server/AyaNova/Controllers/MetricsController.cs @@ -66,6 +66,10 @@ namespace AyaNova.Api.Controllers } string sResult = await GetTheMetrics("plain"); + + //Log + EventLogProcessor.AddEntry(new Event(UserIdFromContext.Id(HttpContext.Items), 0, AyaType.Metrics, AyaEvent.Retrieved), ct); + return Content(sResult); } @@ -91,14 +95,12 @@ namespace AyaNova.Api.Controllers return StatusCode(401, new ApiNotAuthorizedResponse()); } - string sResult = await GetTheMetrics("json"); - - //return Ok(new ApiOkResponse(new { metrics = sResult })); - // /THIS IS NOT RETURNING VALID PARSEABLE JSON, FIX IT - //IDEAS: - //try parsing the result first then return it - // + string sResult = await GetTheMetrics("json"); JObject json = JObject.Parse(sResult); + + //Log + EventLogProcessor.AddEntry(new Event(UserIdFromContext.Id(HttpContext.Items), 0, AyaType.Metrics, AyaEvent.Retrieved), ct); + return Ok(new ApiOkResponse(json)); } diff --git a/server/AyaNova/Controllers/ServerStateController.cs b/server/AyaNova/Controllers/ServerStateController.cs index 895a5414..a2ae33f4 100644 --- a/server/AyaNova/Controllers/ServerStateController.cs +++ b/server/AyaNova/Controllers/ServerStateController.cs @@ -5,6 +5,7 @@ using Microsoft.AspNetCore.Authorization; using Microsoft.Extensions.Logging; using AyaNova.Api.ControllerHelpers; using AyaNova.Biz; +using AyaNova.Models; using System.ComponentModel.DataAnnotations; @@ -19,6 +20,7 @@ namespace AyaNova.Api.Controllers [Produces("application/json")] public class ServerStateController : Controller { + private readonly AyContext ct; private readonly ILogger log; private readonly ApiServerState serverState; @@ -27,8 +29,10 @@ namespace AyaNova.Api.Controllers /// /// /// - public ServerStateController(ILogger logger, ApiServerState apiServerState) + /// + public ServerStateController(ILogger logger, ApiServerState apiServerState, AyContext dbcontext) { + ct = dbcontext; log = logger; serverState = apiServerState; } @@ -90,6 +94,9 @@ namespace AyaNova.Api.Controllers serverState.SetState(desiredState, state.Reason); + //Log + EventLogProcessor.AddEntry(new Event(UserIdFromContext.Id(HttpContext.Items), 0, AyaType.ServerState, AyaEvent.ServerStateChange, $"{state.ServerState}-{state.Reason}"), ct); + return NoContent(); } diff --git a/server/AyaNova/biz/AyaEvent.cs b/server/AyaNova/biz/AyaEvent.cs index 7dc55bc5..122aaf08 100644 --- a/server/AyaNova/biz/AyaEvent.cs +++ b/server/AyaNova/biz/AyaEvent.cs @@ -12,10 +12,15 @@ namespace AyaNova.Biz Deleted = 0, Created = 1, Retrieved = 2, - Modified = 3 + Modified = 3, //specific events - + AttachmentCreate=4, + AttachmentDelete=5, + AttachmentDownload=6, + LicenseFetch=7, + LicenseTrialRequest=8, + ServerStateChange=9 }