diff --git a/API/Controllers/StatsController.cs b/API/Controllers/StatsController.cs deleted file mode 100644 index 09fcf9738..000000000 --- a/API/Controllers/StatsController.cs +++ /dev/null @@ -1,39 +0,0 @@ -using System; -using System.Threading.Tasks; -using API.DTOs.Stats; -using API.Interfaces.Services; -using Microsoft.AspNetCore.Authorization; -using Microsoft.AspNetCore.Mvc; -using Microsoft.Extensions.Logging; - -namespace API.Controllers -{ - public class StatsController : BaseApiController - { - private readonly ILogger _logger; - private readonly IStatsService _statsService; - - public StatsController(ILogger logger, IStatsService statsService) - { - _logger = logger; - _statsService = statsService; - } - - [AllowAnonymous] - [HttpPost("client-info")] - public async Task AddClientInfo([FromBody] ClientInfoDto clientInfoDto) - { - try - { - await _statsService.PathData(clientInfoDto); - - return Ok(); - } - catch (Exception ex) - { - _logger.LogError(ex, "Error updating the usage statistics"); - throw; - } - } - } -} diff --git a/API/DTOs/Stats/InstallationStatsDto.cs b/API/DTOs/Stats/InstallationStatsDto.cs new file mode 100644 index 000000000..bf2d85625 --- /dev/null +++ b/API/DTOs/Stats/InstallationStatsDto.cs @@ -0,0 +1,15 @@ +using System; +using System.Collections.Generic; +using System.Linq; + +namespace API.DTOs.Stats +{ + public class InstallationStatsDto + { + public string InstallId { get; set; } + public string Os { get; set; } + public bool IsDocker { get; set; } + public string DotnetVersion { get; set; } + public string KavitaVersion { get; set; } + } +} diff --git a/API/DTOs/Stats/ServerInfoDto.cs b/API/DTOs/Stats/ServerInfoDto.cs index 2aecebecc..35d5f23f5 100644 --- a/API/DTOs/Stats/ServerInfoDto.cs +++ b/API/DTOs/Stats/ServerInfoDto.cs @@ -1,14 +1,11 @@ -namespace API.DTOs.Stats +namespace API.DTOs.Stats { public class ServerInfoDto { + public string InstallId { get; set; } public string Os { get; set; } - public string DotNetVersion { get; set; } - public string RunTimeVersion { get; set; } - public string KavitaVersion { get; set; } - public string BuildBranch { get; set; } - public string Culture { get; set; } public bool IsDocker { get; set; } - public int NumOfCores { get; set; } + public string DotnetVersion { get; set; } + public string KavitaVersion { get; set; } } } diff --git a/API/DTOs/Stats/UsageStatisticsDto.cs b/API/DTOs/Stats/UsageStatisticsDto.cs deleted file mode 100644 index 08e15dc3b..000000000 --- a/API/DTOs/Stats/UsageStatisticsDto.cs +++ /dev/null @@ -1,33 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; - -namespace API.DTOs.Stats -{ - public class UsageStatisticsDto - { - public UsageStatisticsDto() - { - MarkAsUpdatedNow(); - ClientsInfo = new List(); - } - - public string InstallId { get; set; } - public DateTime LastUpdate { get; set; } - public UsageInfoDto UsageInfo { get; set; } - public ServerInfoDto ServerInfo { get; set; } - public List ClientsInfo { get; set; } - - public void MarkAsUpdatedNow() - { - LastUpdate = DateTime.UtcNow; - } - - public void AddClientInfo(ClientInfoDto clientInfoDto) - { - if (ClientsInfo.Any(x => x.IsTheSameDevice(clientInfoDto))) return; - - ClientsInfo.Add(clientInfoDto); - } - } -} diff --git a/API/Interfaces/Services/IStatsService.cs b/API/Interfaces/Services/IStatsService.cs index 19d8d9f4b..841544fd9 100644 --- a/API/Interfaces/Services/IStatsService.cs +++ b/API/Interfaces/Services/IStatsService.cs @@ -1,11 +1,10 @@ -using System.Threading.Tasks; +using System.Threading.Tasks; using API.DTOs.Stats; namespace API.Interfaces.Services { public interface IStatsService { - Task PathData(ClientInfoDto clientInfoDto); Task CollectAndSendStatsData(); } } diff --git a/API/Services/Clients/StatsApiClient.cs b/API/Services/Clients/StatsApiClient.cs index 52d1c9fcf..0cc37eb2a 100644 --- a/API/Services/Clients/StatsApiClient.cs +++ b/API/Services/Clients/StatsApiClient.cs @@ -22,7 +22,7 @@ namespace API.Services.Clients _client.Timeout = TimeSpan.FromSeconds(30); } - public async Task SendDataToStatsServer(UsageStatisticsDto data) + public async Task SendDataToStatsServer(InstallationStatsDto data) { var responseContent = string.Empty; diff --git a/API/Services/Tasks/StatsService.cs b/API/Services/Tasks/StatsService.cs index 47087fc8d..7067b38ef 100644 --- a/API/Services/Tasks/StatsService.cs +++ b/API/Services/Tasks/StatsService.cs @@ -1,9 +1,7 @@ using System; using System.IO; -using System.Linq; using System.Runtime.InteropServices; using System.Text.Json; -using System.Threading; using System.Threading.Tasks; using API.Data; using API.DTOs.Stats; @@ -12,16 +10,12 @@ using API.Interfaces.Services; using API.Services.Clients; using Kavita.Common; using Kavita.Common.EnvironmentInfo; -using Microsoft.EntityFrameworkCore; using Microsoft.Extensions.Logging; namespace API.Services.Tasks { public class StatsService : IStatsService { - private const string TempFilePath = "stats/"; - private const string TempFileName = "app_stats.json"; - private readonly StatsApiClient _client; private readonly DataContext _dbContext; private readonly ILogger _logger; @@ -35,54 +29,22 @@ namespace API.Services.Tasks _logger = logger; _unitOfWork = unitOfWork; } - - private static string FinalPath => Path.Combine(Directory.GetCurrentDirectory(), TempFilePath, TempFileName); - private static bool FileExists => File.Exists(FinalPath); - - public async Task PathData(ClientInfoDto clientInfoDto) + #region Communcation Methods + private async Task CollectAndSendRelevantData() { - _logger.LogDebug("Pathing client data to the file"); - - var statisticsDto = await GetData(); - - statisticsDto.AddClientInfo(clientInfoDto); - - await SaveFile(statisticsDto); - } - - private async Task CollectRelevantData() - { - _logger.LogDebug("Collecting data from the server and database"); - - _logger.LogDebug("Collecting usage info"); - var usageInfo = await GetUsageInfo(); - _logger.LogDebug("Collecting server info"); - var serverInfo = GetServerInfo(); - await PathData(serverInfo, usageInfo); + var data = await GetData(); + + _logger.LogDebug("Sending data to the Stats server"); + + await _client.SendDataToStatsServer(data); } - private async Task FinalizeStats() - { - try - { - _logger.LogDebug("Finalizing Stats collection flow"); + #endregion - var data = await GetExistingData(); - _logger.LogDebug("Sending data to the Stats server"); - await _client.SendDataToStatsServer(data); - - _logger.LogDebug("Deleting the file from disk"); - if (FileExists) File.Delete(FinalPath); - } - catch (Exception ex) - { - _logger.LogError(ex, "Error Finalizing Stats collection flow"); - throw; - } - } + #region Data Collection public async Task CollectAndSendStatsData() { @@ -92,103 +54,40 @@ namespace API.Services.Tasks _logger.LogDebug("User has opted out of stat collection, not registering tasks"); return; } - await CollectRelevantData(); - await FinalizeStats(); + await CollectAndSendRelevantData(); + } - private async Task PathData(ServerInfoDto serverInfoDto, UsageInfoDto usageInfoDto) + private async ValueTask GetData() { - _logger.LogDebug("Pathing server and usage info to the file"); - var data = await GetData(); - - data.ServerInfo = serverInfoDto; - data.UsageInfo = usageInfoDto; - - data.MarkAsUpdatedNow(); - - await SaveFile(data); - } - - private async ValueTask GetData() - { - if (!FileExists) return new UsageStatisticsDto {InstallId = HashUtil.AnonymousToken()}; - - return await GetExistingData(); - } - - private async Task GetUsageInfo() - { - var usersCount = await _dbContext.Users.CountAsync(); - - var libsCountByType = await _dbContext.Library - .AsNoTracking() - .GroupBy(x => x.Type) - .Select(x => new LibInfo {Type = x.Key, Count = x.Count()}) - .ToArrayAsync(); - - var uniqueFileTypes = await _unitOfWork.FileRepository.GetFileExtensions(); - - var usageInfo = new UsageInfoDto + return new InstallationStatsDto { - UsersCount = usersCount, - LibraryTypesCreated = libsCountByType, - FileTypes = uniqueFileTypes + Os = RuntimeInformation.OSDescription, + DotnetVersion = Environment.Version.ToString(), + KavitaVersion = BuildInfo.Version.ToString(), + InstallId = HashUtil.AnonymousToken(), + IsDocker = new OsInfo(Array.Empty()).IsDocker }; - return usageInfo; + } + public static ServerInfoDto GetServerInfo() { var serverInfo = new ServerInfoDto { Os = RuntimeInformation.OSDescription, - DotNetVersion = Environment.Version.ToString(), - RunTimeVersion = RuntimeInformation.FrameworkDescription, + DotnetVersion = Environment.Version.ToString(), KavitaVersion = BuildInfo.Version.ToString(), - Culture = Thread.CurrentThread.CurrentCulture.Name, - BuildBranch = BuildInfo.Branch, - IsDocker = new OsInfo(Array.Empty()).IsDocker, - NumOfCores = Environment.ProcessorCount + InstallId = HashUtil.AnonymousToken(), + IsDocker = new OsInfo(Array.Empty()).IsDocker + }; return serverInfo; } - - private async Task GetExistingData() - { - _logger.LogInformation("Fetching existing data from file"); - var existingDataJson = await GetFileDataAsString(); - - _logger.LogInformation("Deserializing data from file to object"); - var existingData = JsonSerializer.Deserialize(existingDataJson); - - return existingData; - } - - private async Task GetFileDataAsString() - { - _logger.LogInformation("Reading file from disk"); - return await File.ReadAllTextAsync(FinalPath); - } - - private async Task SaveFile(UsageStatisticsDto statisticsDto) - { - _logger.LogDebug("Saving file"); - - var finalDirectory = FinalPath.Replace(TempFileName, string.Empty); - if (!Directory.Exists(finalDirectory)) - { - _logger.LogDebug("Creating tmp directory"); - Directory.CreateDirectory(finalDirectory); - } - - _logger.LogDebug("Serializing data to write"); - var dataJson = JsonSerializer.Serialize(statisticsDto); - - _logger.LogDebug("Writing file to the disk"); - await File.WriteAllTextAsync(FinalPath, dataJson); - } + #endregion } }