diff --git a/Oqtane.Client/Modules/Admin/Logs/Index.razor b/Oqtane.Client/Modules/Admin/Logs/Index.razor index cf2f7cbc..8d180f13 100644 --- a/Oqtane.Client/Modules/Admin/Logs/Index.razor +++ b/Oqtane.Client/Modules/Admin/Logs/Index.razor @@ -86,47 +86,48 @@ else
- + + } @code { - private string _level = "-"; - private string _function = "-"; - private string _rows = "10"; - private int _page = 1; - private List _logs; - private int _retention = 30; + private string _level = "-"; + private string _function = "-"; + private string _rows = "10"; + private int _page = 1; + private List _logs; + private int _retention = 30; - public override string UrlParametersTemplate => "/{level}/{function}/{rows}/{page}"; - public override SecurityAccessLevel SecurityAccessLevel => SecurityAccessLevel.Host; + public override string UrlParametersTemplate => "/{level}/{function}/{rows}/{page}"; + public override SecurityAccessLevel SecurityAccessLevel => SecurityAccessLevel.Host; - protected override async Task OnParametersSetAsync() - { - try - { - if (UrlParameters.ContainsKey("level")) - { - _level = UrlParameters["level"]; - } - if (UrlParameters.ContainsKey("function")) - { - _function = UrlParameters["function"]; - } - if (UrlParameters.ContainsKey("rows")) - { - _rows = UrlParameters["rows"]; - } - if (UrlParameters.ContainsKey("page") && int.TryParse(UrlParameters["page"], out int page)) - { - _page = page; - } + protected override async Task OnParametersSetAsync() + { + try + { + if (UrlParameters.ContainsKey("level")) + { + _level = UrlParameters["level"]; + } + if (UrlParameters.ContainsKey("function")) + { + _function = UrlParameters["function"]; + } + if (UrlParameters.ContainsKey("rows")) + { + _rows = UrlParameters["rows"]; + } + if (UrlParameters.ContainsKey("page") && int.TryParse(UrlParameters["page"], out int page)) + { + _page = page; + } await GetLogs(); - var settings = await SettingService.GetSiteSettingsAsync(PageState.Site.SiteId); - _retention = int.Parse( SettingService.GetSetting(settings, "LogRetention", "30")); + var settings = await SettingService.GetSiteSettingsAsync(PageState.Site.SiteId); + _retention = int.Parse( SettingService.GetSetting(settings, "LogRetention", "30")); } catch (Exception ex) { @@ -213,22 +214,37 @@ else return classname; } - private async Task SaveSiteSettings() - { - try - { - var settings = await SettingService.GetSiteSettingsAsync(PageState.Site.SiteId); - settings = SettingService.SetSetting(settings, "LogRetention", _retention.ToString(), true); + private async Task SaveSiteSettings() + { + try + { + var settings = await SettingService.GetSiteSettingsAsync(PageState.Site.SiteId); + settings = SettingService.SetSetting(settings, "LogRetention", _retention.ToString(), true); await SettingService.UpdateSiteSettingsAsync(settings, PageState.Site.SiteId); - AddModuleMessage(Localizer["Success.SaveSiteSettings"], MessageType.Success); - } - catch (Exception ex) - { - await logger.LogError(ex, "Error Saving Site Settings {Error}", ex.Message); - AddModuleMessage(Localizer["Error.SaveSiteSettings"], MessageType.Error); - } - } + AddModuleMessage(Localizer["Success.SaveSiteSettings"], MessageType.Success); + } + catch (Exception ex) + { + await logger.LogError(ex, "Error Saving Site Settings {Error}", ex.Message); + AddModuleMessage(Localizer["Error.SaveSiteSettings"], MessageType.Error); + } + } + + private async Task ClearLogs() + { + try + { + await LogService.ClearLogsAsync(PageState.Site.SiteId); + await GetLogs(); + StateHasChanged(); + } + catch (Exception ex) + { + await logger.LogError(ex, "Error Clearing Logs {Error}", ex.Message); + AddModuleMessage(Localizer["Error.ClearLogs"], MessageType.Error); + } + } private void OnPageChange(int page) { diff --git a/Oqtane.Client/Resources/Modules/Admin/Logs/Index.resx b/Oqtane.Client/Resources/Modules/Admin/Logs/Index.resx index dfd55880..27b3c4c7 100644 --- a/Oqtane.Client/Resources/Modules/Admin/Logs/Index.resx +++ b/Oqtane.Client/Resources/Modules/Admin/Logs/Index.resx @@ -210,4 +210,13 @@ Settings Saved Successfully + + Clear Logs + + + Are you sure you wish to clear all the logs? + + + Error Clearing Logs + \ No newline at end of file diff --git a/Oqtane.Client/Services/Interfaces/ILogService.cs b/Oqtane.Client/Services/Interfaces/ILogService.cs index 2a821359..dea788bc 100644 --- a/Oqtane.Client/Services/Interfaces/ILogService.cs +++ b/Oqtane.Client/Services/Interfaces/ILogService.cs @@ -29,6 +29,13 @@ namespace Oqtane.Services /// Task GetLogAsync(int logId); + /// + /// Clear the entire logs of the given site. + /// + /// + /// + Task ClearLogsAsync(int siteId); + /// /// Creates a new log entry /// diff --git a/Oqtane.Client/Services/LogService.cs b/Oqtane.Client/Services/LogService.cs index e575694e..26c37038 100644 --- a/Oqtane.Client/Services/LogService.cs +++ b/Oqtane.Client/Services/LogService.cs @@ -35,6 +35,11 @@ namespace Oqtane.Services return await GetJsonAsync($"{Apiurl}/{logId}"); } + public async Task ClearLogsAsync(int siteId) + { + await DeleteAsync($"{Apiurl}?siteid={siteId}"); + } + public async Task Log(int? pageId, int? moduleId, int? userId, string category, string feature, LogFunction function, LogLevel level, Exception exception, string message, params object[] args) { await Log(null, pageId, moduleId, userId, category, feature, function, level, exception, message, args); diff --git a/Oqtane.Server/Controllers/LogController.cs b/Oqtane.Server/Controllers/LogController.cs index 34fc5b7c..c35bf6bf 100644 --- a/Oqtane.Server/Controllers/LogController.cs +++ b/Oqtane.Server/Controllers/LogController.cs @@ -76,5 +76,20 @@ namespace Oqtane.Controllers HttpContext.Response.StatusCode = (int)HttpStatusCode.Forbidden; } } + + [HttpDelete] + [Authorize(Roles = RoleNames.Admin)] + public void Delete(string siteId) + { + if (int.TryParse(siteId, out int parsedSiteId) && parsedSiteId == _alias.SiteId) + { + _logs.ClearLogs(parsedSiteId); + } + else + { + _logger.Log(LogLevel.Error, this, LogFunction.Security, "Unauthorized Log Delete Attempt {SiteId}", siteId); + HttpContext.Response.StatusCode = (int)HttpStatusCode.Forbidden; + } + } } } diff --git a/Oqtane.Server/Repository/Interfaces/ILogRepository.cs b/Oqtane.Server/Repository/Interfaces/ILogRepository.cs index 918785f2..623e3a2d 100644 --- a/Oqtane.Server/Repository/Interfaces/ILogRepository.cs +++ b/Oqtane.Server/Repository/Interfaces/ILogRepository.cs @@ -9,5 +9,6 @@ namespace Oqtane.Repository Log GetLog(int logId); void AddLog(Log log); int DeleteLogs(int siteId, int age); + void ClearLogs(int siteId); } } diff --git a/Oqtane.Server/Repository/LogRepository.cs b/Oqtane.Server/Repository/LogRepository.cs index 6c388f15..81d5813f 100644 --- a/Oqtane.Server/Repository/LogRepository.cs +++ b/Oqtane.Server/Repository/LogRepository.cs @@ -71,5 +71,19 @@ namespace Oqtane.Repository } return count; } + + public void ClearLogs(int siteId) + { + using var db = _dbContextFactory.CreateDbContext(); + var getLogsForDelete = () => db.Log.Where(item => item.SiteId == siteId).Take(100).ToList(); + // delete logs in batches of 100 records + var logs = getLogsForDelete(); + while (logs.Count > 0) + { + db.Log.RemoveRange(logs); + db.SaveChanges(); + logs = getLogsForDelete(); + } + } } } diff --git a/Oqtane.Server/wwwroot/css/app.css b/Oqtane.Server/wwwroot/css/app.css index 863d10d2..f5a65a80 100644 --- a/Oqtane.Server/wwwroot/css/app.css +++ b/Oqtane.Server/wwwroot/css/app.css @@ -35,6 +35,9 @@ app { } /* Action Dialog */ +.app-actiondialog{ + position: absolute; +} .app-actiondialog .modal { position: fixed; /* Stay in place */ z-index: 9999; /* Sit on top */