diff --git a/ModVersionChecker/Main.cs b/ModVersionChecker/Main.cs index ea304b6..1827068 100644 --- a/ModVersionChecker/Main.cs +++ b/ModVersionChecker/Main.cs @@ -1,9 +1,11 @@ using Microsoft.Extensions.DependencyInjection; -using ModVersionChecker.forms; using Microsoft.Extensions.Hosting; -using ModVersionChecker.managers.interfaces; using ModVersionChecker.managers.filesystem; +using ModVersionChecker.managers.interfaces; using ModVersionChecker.managers.litedb; +using ModVersionChecker.service; +using ModVersionChecker.service.interfaces; +using ModVersionChecker.ui.forms; namespace ModVersionChecker { @@ -18,13 +20,15 @@ namespace ModVersionChecker builder.ConfigureServices(services => { services.AddSingleton(); - services.AddSingleton(); + services.AddSingleton(); services.AddSingleton(); - services.AddSingleton(); - services.AddSingleton(); + services.AddSingleton(); + services.AddSingleton(); services.AddSingleton(); services.AddSingleton(); services.AddSingleton(); + services.AddSingleton(); + services.AddSingleton(); services.AddTransient(); services.AddTransient(); @@ -55,9 +59,9 @@ namespace ModVersionChecker var serviceProvider = host.Services; var configForm = serviceProvider.GetService(); - var versionChecker = serviceProvider.GetService(); var notifyIconService = serviceProvider.GetRequiredService(); var configManager = serviceProvider.GetRequiredService(); + var versionService = serviceProvider.GetRequiredService(); var config = configManager.GetConfig(); EventHandler openFormHandler = (s, e) => @@ -94,39 +98,19 @@ namespace ModVersionChecker notifyIcon.DoubleClick += openFormHandler; bool checkOnInitialStart = config.CheckOnStartup; - if (checkOnInitialStart && versionChecker != null) + if (checkOnInitialStart) { - versionChecker.StartVersionChecking(notifyIcon); - versionChecker.OnFinished += (s, e) => { + + Task.Run(async () => + { + await versionService.CheckAllApps(); if (configForm != null) { - if (configForm.InvokeRequired) - { - configForm.Invoke(() => configForm.UpdateListView()); - } - else - { - configForm.UpdateListView(); - } + configForm.UpdateListView(); } - }; - } - - - if (versionChecker != null) - { - - if (configForm != null) - { - configForm.OnRecheck += (s, e) => - { - if (versionChecker != null) - { - versionChecker.CheckAsync(); - } - }; - } - } + }); + + } host.Start(); diff --git a/ModVersionChecker/ModVersionChecker.csproj b/ModVersionChecker/ModVersionChecker.csproj index 9174edd..7aebcc0 100644 --- a/ModVersionChecker/ModVersionChecker.csproj +++ b/ModVersionChecker/ModVersionChecker.csproj @@ -10,6 +10,7 @@ + @@ -20,23 +21,12 @@ + - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest + + Always PreserveNewest @@ -52,8 +42,4 @@ - - - - \ No newline at end of file diff --git a/ModVersionChecker/VersionChecker.cs b/ModVersionChecker/VersionChecker.cs index 514e8e4..0e36076 100644 --- a/ModVersionChecker/VersionChecker.cs +++ b/ModVersionChecker/VersionChecker.cs @@ -1,7 +1,6 @@ -using ModVersionChecker.data.model; +using ModVersionChecker.model; using ModVersionChecker.managers.interfaces; -using ModVersionChecker.utils; -using NuGet.Versioning; +using ModVersionChecker.service.interfaces; namespace ModVersionChecker @@ -9,46 +8,33 @@ namespace ModVersionChecker public class VersionChecker { private readonly IConfigManager _configManager; - private readonly IAppsManager _appsManager; - private readonly ISourcesDefManager _sourcesDefManager; - private readonly INotifyIconService _notifyIconService; - private readonly IFlightSimsManager _fsManager; - private List errorMessages = new List(); - private List updateMessages = new List(); - private NotifyIcon? _notifyIcon; + private readonly IVersionService _versionService; public event EventHandler? OnFinished; public VersionChecker( - IConfigManager configManager, - IAppsManager appsManager, - ISourcesDefManager sourcesDefManager, - INotifyIconService notifyIconService, - IFlightSimsManager fsManager) + IVersionService versionService, + IConfigManager configManager) { _configManager = configManager ?? throw new ArgumentNullException(nameof(configManager)); - _appsManager = appsManager ?? throw new ArgumentNullException(nameof(appsManager)); - _sourcesDefManager = sourcesDefManager ?? throw new ArgumentNullException(nameof(sourcesDefManager)); - _notifyIconService = notifyIconService ?? throw new ArgumentNullException(nameof(notifyIconService)); - _fsManager = fsManager ?? throw new ArgumentNullException(nameof(fsManager)); + _versionService = versionService ?? throw new ArgumentNullException(nameof(versionService)); } - private void HandleAppError(string message, AppConfig app) - { - errorMessages.Add(message); - _appsManager.UpdateStatus(app, AppStatus.Error); - } + //private void HandleAppError(string message, AppConfig app) + //{ + // errorMessages.Add(message); + // _appsManager.UpdateStatus(app, AppStatus.ERROR); + //} - public void StartVersionChecking(NotifyIcon notifyIcon) + public void StartVersionChecking() { - var config = _configManager.Load() ?? new GlobalConfig(); - _notifyIcon = notifyIcon ?? throw new ArgumentNullException(nameof(notifyIcon)); + var config = _configManager.Load() ?? new Config(); // Run version checks in a background thread new Thread(async () => { while (true) { - await CheckAsync(); + await _versionService.CheckAllApps(); Thread.Sleep(config.IntervalMinutes * 60 * 1000); } @@ -56,87 +42,95 @@ namespace ModVersionChecker }) { IsBackground = true }.Start(); } - - public async Task CheckAsync() - { - var config = _configManager.Load() ?? new GlobalConfig(); - var apps = _appsManager.Load() ?? new List(); - var sources = _sourcesDefManager.List() ?? new List(); - var fsMods = _fsManager.Load() ?? new List(); - updateMessages = new List(); - errorMessages = new List(); + - foreach (AppConfig app in apps) - { - if (app.Status != AppStatus.Error && app.LastCheckedAt != 0 && app.LastCheckedAt < TimeUtils.GetUnixTimeMillis(DateTime.Now.AddMinutes(-60))) - continue; + //public async Task CheckAsync() + //{ + // var config = _configManager.Load() ?? new GlobalConfig(); + // var apps = _appsManager.Load() ?? new List(); + // var sources = _sourcesDefManager.List() ?? new List(); + // var fsMods = _fsManager.Load() ?? new List(); + // var types = _typeConfigManager.GetTypeConfigs() ?? new List(); - var status = AppStatus.None; - var sourceId = app.Source; - if (string.IsNullOrWhiteSpace(sourceId)) - { - HandleAppError($"{app.Name} has no source configured.", app); - continue; - } - var source = sources.FirstOrDefault(s => s.Id == sourceId); - if (source == null) - { - HandleAppError($"{app.Name} has an invalid source: {sourceId}", app); - continue; - } - try - { - foreach (var fsVersion in app.MsfsVersions) - { - var fsConfig = _fsManager.GetByShortName(fsVersion); - if (fsConfig == null) - { - HandleAppError($"{app.Name} has no FS mod path configured for version {fsVersion}.", app); - continue; - } - var checker = CheckerFactory.CreateChecker(source.Type); - var current = NuGetVersion.Parse(VersionUtils.GetCurrentVersion(app, fsConfig)); - var latest = NuGetVersion.Parse(await checker.GetLatestVersion(app.Params, source)); + // var appVersionsMap = await _apiVersionService.GetAppVersionsAsync(apps); - app.CurrentVersion = current.ToString(); - app.LatestVersion = latest.ToString(); + // updateMessages = new List(); + // errorMessages = new List(); - if (latest.CompareTo(current) == 1) - { - updateMessages.Add($"{app.Name}: New version {latest} (current: {current})"); - status = AppStatus.UpdateAvailable; - } - } - _appsManager.UpdateStatus(app, status); + // foreach (AppConfig app in apps) + // { + // if (app.Status != AppStatus.ERROR && app.LastCheckedAt != 0 && app.LastCheckedAt < TimeUtils.GetUnixTimeMillis(DateTime.Now.AddMinutes(-60))) + // continue; + // var status = AppStatus.NONE; + // var sourceId = app.Source; - } - catch (Exception ex) - { - HandleAppError($"Failed for {app.Name}: {ex.Message}", app); - } - } + // // Skip apps that are not in the API response - if (updateMessages.Count > 0) - { - _notifyIconService.ShowBalloonTip( - 10000, - "Updates Available", - string.Join("\n", updateMessages), - ToolTipIcon.Info - ); - } - if (errorMessages.Count > 0) - { - _notifyIconService.ShowBalloonTip( - 10000, - "Errors", - string.Join("\n", errorMessages), - ToolTipIcon.Error - ); - } - OnFinished?.Invoke(this, "Version check completed."); - } + // if (!appVersionsMap.Any(a => app.Id == a.Id)) { + // continue; + // } + // var latesstVersion = appVersionsMap.FirstOrDefault(a => a.Id == app.Id).LatestVersion; + // if (string.IsNullOrWhiteSpace(sourceId)) + // { + // HandleAppError($"{app.Name} has no source configured.", app); + // continue; + // } + // var source = sources.FirstOrDefault(s => s.Id == sourceId); + // if (source == null) + // { + // HandleAppError($"{app.Name} has an invalid source: {sourceId}", app); + // continue; + // } + // try + // { + // var type = app.Types[0]; + // var typeConfig = types[0]; + // if (typeConfig == null) + // { + // HandleAppError($"{app.Name} has no FS mod path configured for version {type}.", app); + // continue; + // } + // var current = NuGetVersion.Parse(VersionUtils.GetCurrentVersion(app, typeConfig)); + // var latest = NuGetVersion.Parse(latesstVersion); + + // app.CurrentVersion = current.ToString(); + // app.LatestVersion = latest.ToString(); + + // if (latest.CompareTo(current) == 1) + // { + // updateMessages.Add($"{app.Name}: New version {latest} (current: {current})"); + // status = AppStatus.UPDATE_AVAILABLE; + // } + + // _appsManager.UpdateStatus(app, status); + // } + // catch (Exception ex) + // { + // HandleAppError($"Failed for {app.Name}: {ex.Message}", app); + // } + // } + + // if (updateMessages.Count > 0) + // { + // _notifyIconService.ShowBalloonTip( + // 10000, + // "Updates Available", + // string.Join("\n", updateMessages), + // ToolTipIcon.Info + // ); + // } + // if (errorMessages.Count > 0) + // { + // _notifyIconService.ShowBalloonTip( + // 10000, + // "Errors", + // string.Join("\n", errorMessages), + // ToolTipIcon.Error + // ); + // } + // OnFinished?.Invoke(this, "Version check completed."); + //} } } diff --git a/ModVersionChecker/checkers/ApiChecker.cs b/ModVersionChecker/checkers/ApiChecker.cs deleted file mode 100644 index d27de40..0000000 --- a/ModVersionChecker/checkers/ApiChecker.cs +++ /dev/null @@ -1,56 +0,0 @@ -using ModVersionChecker.data.model; -using System; -using System.Net.Http; -using System.Text.Json; -using System.Threading.Tasks; - -namespace ModVersionChecker -{ - public class ApiChecker : IVersionChecker - { - private static readonly HttpClient _httpClient = new HttpClient(); - - public async Task GetLatestVersion(Dictionary paramsDict, SourceDef source) - { - if (!paramsDict.TryGetValue("url", out var url) || string.IsNullOrEmpty(url)) - { - throw new ArgumentException("API URL required"); - } - if (!paramsDict.TryGetValue("jsonPath", out var jsonPath) || string.IsNullOrEmpty(jsonPath)) - { - throw new ArgumentException("jsonPath required"); - } - - var response = await _httpClient.GetAsync(url); - if (!response.IsSuccessStatusCode) - { - throw new Exception($"API error: {(int)response.StatusCode} {response.ReasonPhrase}"); - } - - var body = await response.Content.ReadAsStringAsync(); - if (string.IsNullOrEmpty(body)) - { - throw new Exception("Empty API response"); - } - - using var jsonDoc = JsonDocument.Parse(body); - var element = jsonDoc.RootElement; - - foreach (var key in jsonPath.Split('.')) - { - if (!element.TryGetProperty(key, out var nextElement)) - { - throw new Exception($"JSON key '{key}' not found in response"); - } - element = nextElement; - } - - if (element.ValueKind != JsonValueKind.String) - { - throw new Exception($"JSON value for '{jsonPath}' is not a string"); - } - - return element.GetString()!.Trim(); - } - } -} \ No newline at end of file diff --git a/ModVersionChecker/checkers/CheckerFactory.cs b/ModVersionChecker/checkers/CheckerFactory.cs deleted file mode 100644 index 0b9fdfd..0000000 --- a/ModVersionChecker/checkers/CheckerFactory.cs +++ /dev/null @@ -1,17 +0,0 @@ -namespace ModVersionChecker -{ - public static class CheckerFactory - { - public static IVersionChecker CreateChecker(string type) - { - string[] parts = type.Split(':'); - - return parts[0].ToLower() switch - { - "scrape" => new ScrapeChecker(), - "api" => new ApiChecker(), - _ => throw new ArgumentException($"Unknown checker type: {type}") - }; - } - } -} \ No newline at end of file diff --git a/ModVersionChecker/checkers/ScrapeChecker.cs b/ModVersionChecker/checkers/ScrapeChecker.cs deleted file mode 100644 index 99adc8c..0000000 --- a/ModVersionChecker/checkers/ScrapeChecker.cs +++ /dev/null @@ -1,94 +0,0 @@ -using System.Text.RegularExpressions; -using ModVersionChecker.data.model; -using OpenQA.Selenium; -using OpenQA.Selenium.Chrome; - -namespace ModVersionChecker -{ - public class ScrapeChecker : IVersionChecker - { - public async Task GetLatestVersion(Dictionary paramsDict, SourceDef source) - { - if (!paramsDict.TryGetValue("url", out var url) || string.IsNullOrEmpty(url)) - { - throw new ArgumentException("URL required"); - } - - var mode = GetValueOrDefault(paramsDict, "mode", source); - var response = ""; - if (mode == "selenium") - { - response = await SeleniumFetch(url); - } - else - { - response = await DefaultFetch(url); ; - } - - - string pattern = @">\s+<"; - response = Regex.Replace(response, pattern, "><"); - var regex = GetValueOrDefault(paramsDict, "regex", source); - - var match = System.Text.RegularExpressions.Regex.Match(response, regex); - if (!match.Success || match.Groups.Count < 2) - { - throw new Exception($"No match with regex in response"); - } - return match.Groups[1].Value; - - } - - private string GetValueOrDefault(Dictionary dict, string key, SourceDef source) - { - var value = ""; - if (dict.ContainsKey(key) && !string.IsNullOrEmpty(dict[key])) - { - value = dict[key]; - } - else if (source.Defaults != null && source.Defaults.ContainsKey(key)) - { - value = source.Defaults[key]; - } - return value; - } - - private Task DefaultFetch(string url) - { - var httpClient = new HttpClient(); - httpClient.DefaultRequestHeaders.Add("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36"); - httpClient.DefaultRequestHeaders.Add("Accept", "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8"); - httpClient.DefaultRequestHeaders.Add("Accept-Language", "en-US,en;q=0.5"); - - return httpClient.GetStringAsync(url); - } - - private async Task SeleniumFetch(string url) - { - var service = ChromeDriverService.CreateDefaultService(); - service.HideCommandPromptWindow = true; - - var options = new ChromeOptions(); - options.AddArgument("--headless"); // Run in headless mode - options.AddArgument("--disable-gpu"); - options.AddArgument("--no-sandbox"); - options.AddArgument("--user-agent=Mozilla/5.0 (Windows NT 10.0; Win64; x64) Chrome/91.0.4472.124"); - - using var driver = new ChromeDriver(service, options); - try - { - driver.Navigate().GoToUrl(url); - // Wait for the page to load - await Task.Delay(2000); // Adjust as necessary - // Example: Get the page source - var pageSource = driver.PageSource; - // Close the driver - return pageSource; - } - finally - { - driver.Quit(); - } - } - } -} \ No newline at end of file diff --git a/ModVersionChecker/checkers/VersionChecker.cs b/ModVersionChecker/checkers/VersionChecker.cs deleted file mode 100644 index ead2e3f..0000000 --- a/ModVersionChecker/checkers/VersionChecker.cs +++ /dev/null @@ -1,9 +0,0 @@ -using ModVersionChecker.data.model; - -namespace ModVersionChecker -{ - public interface IVersionChecker - { - Task GetLatestVersion(Dictionary paramsDict, SourceDef source); - } -} \ No newline at end of file diff --git a/ModVersionChecker/config.yaml b/ModVersionChecker/config.yaml new file mode 100644 index 0000000..826b689 --- /dev/null +++ b/ModVersionChecker/config.yaml @@ -0,0 +1,3 @@ +api: + base-url: http://192.168.1.115:3115/api + # base-url: http://localhost:8080/api \ No newline at end of file diff --git a/ModVersionChecker/data/apps - Copy.json b/ModVersionChecker/data/apps - Copy.json deleted file mode 100644 index 5573c7d..0000000 --- a/ModVersionChecker/data/apps - Copy.json +++ /dev/null @@ -1,55 +0,0 @@ -[ - { - "name": "PMS50 GTN750", - "msfsVersions": [ "msfs2024" ], - "source": "pms50_gtn750", - "params": { - "url": "https://pms50.com/msfs/" - }, - "currentVersionConfig": { - "package": "pms50-instrument-gtn750" - } - }, - { - "name": "Teikof SKMZ", - "msfsVersions": [ "msfs2024" ], - "source": "sim_market", - "params": { - "url": "https://secure.simmarket.com/teikof-studio-skmz-la-nubia-airport-msfs.phtml" - }, - "currentVersionConfig": { - "package": "teikofstudio-airport-skmz-manizales" - } - }, - { - "name": "SWS", - "msfsVersions": [ - "msfs2024" - ], - "source": "sws", - "params": { - "url": "https://simworksstudios.com/product/kodiak-100-series-ii/", - "regex": "Current Version: (\\d\u002B\\.\\d\u002B\\.\\d\u002B)" - }, - "currentVersionConfig": { - "package": "sws-aircraft-kodiak-wheels", - "version": "" - } - }, - { - "name": "GSX Pro", - "msfsVersions": [ - "msfs2024" - ], - "source": "custom", - "params": { - "url": "https://www.fsdreamteam.com/couatl_liveupdate_notes.html", - "regex": "

Version (\\d\u002B\\.\\d\u002B\\.\\d\u002B) –" - }, - "currentVersionConfig": { - "package": "fsdreamteam-gsx-pro", - "version": "" - } - } - - ] \ No newline at end of file diff --git a/ModVersionChecker/data/apps.json b/ModVersionChecker/data/apps.json deleted file mode 100644 index d366e4b..0000000 --- a/ModVersionChecker/data/apps.json +++ /dev/null @@ -1,86 +0,0 @@ -[ - { - "id": "8", - "name": "PMS50 GTN750", - "msfsVersions": [ - "msfs2024" - ], - "source": "pms50_gtn750", - "params": { - "url": "https://pms50.com/msfs/", - "regex": "Current version: (\\d\u002B\\.\\d\u002B\\.\\d\u002B)" - }, - "fsFields": { - "msfs2024": { - "package": "pms50-instrument-gtn750" - } - } - }, - { - "id": "2", - "name": "Teikof SKMZ", - "msfsVersions": [ - "msfs2024" - ], - "source": "sim_market", - "params": { - "url": "https://secure.simmarket.com/teikof-studio-skmz-la-nubia-airport-msfs.phtml" - }, - "fsFields": { - "msfs2024": { - "package": "teikofstudio-airport-skmz-manizales" - } - } - }, - { - "id": "3", - "name": "SWS", - "msfsVersions": [ - "msfs2024" - ], - "source": "sws", - "params": { - "url": "https://simworksstudios.com/product/kodiak-100-series-ii/", - "regex": "Current Version: (\\d\u002B\\.\\d\u002B\\.\\d\u002B)" - }, - "fsFields": { - "msfs2024": { - "package": "sws-aircraft-kodiak-wheels" - } - } - }, - { - "id": "4", - "name": "GSX Pro", - "msfsVersions": [ - "msfs2024" - ], - "source": "custom", - "params": { - "url": "https://www.fsdreamteam.com/couatl_liveupdate_notes.html", - "regex": "\u003Cp\u003EVersion (\\d\u002B\\.\\d\u002B\\.\\d\u002B) \u2013" - }, - "fsFields": { - "msfs2024": { - "package": "fsdreamteam-gsx-pro" - } - } - }, - { - "id": "5", - "name": "Aerostar 600", - "msfsVersions": [ - "msfs2024" - ], - "source": "a2a", - "params": { - "url": "https://a2asimulations.com/forum/viewforum.php?f=153", - "regex": "Accu-Sim Aerostar 600 \u2013 v(\\d\u002B\\.\\d\u002B\\.\\d\u002B)" - }, - "fsFields": { - "msfs2024": { - "package": "a2a-aircraft-aerostar600" - } - } - } -] \ No newline at end of file diff --git a/ModVersionChecker/data/checkerTypesDef.json b/ModVersionChecker/data/checkerTypesDef.json deleted file mode 100644 index cf284db..0000000 --- a/ModVersionChecker/data/checkerTypesDef.json +++ /dev/null @@ -1,33 +0,0 @@ - [ - { - "name": "scrape", - "params": [ - { - "name": "url", - "label": "Url", - "type": "string", - "required": true - }, - { - "label": "Regex", - "name": "regex", - "type": "string" - }, { - "label": "Mode", - "name": "mode", - "type": "string" - } - ] - }, - { - "name": "api", - "params": [ - { - "label": "Url", - "name": "url", - "type": "string", - "required": true - } - ] - } - ] \ No newline at end of file diff --git a/ModVersionChecker/data/config.json b/ModVersionChecker/data/config.json deleted file mode 100644 index 49ebcc0..0000000 --- a/ModVersionChecker/data/config.json +++ /dev/null @@ -1,21 +0,0 @@ -{ - "intervalMinutes": 60, - "checkOnStartup": false, - "fsModPaths": { - "msfs2024": { - "path": "I:/Microsoft Flight Simulator 2024/Packages/Community/", - "file": "manifest.json", - "fileType": "json", - "key": "package_version", - "fields": [ - { - "name": "package", - "label": "Package Name", - "type": "string", - "control": "directory", - "required": true - } - ] - } - } -} \ No newline at end of file diff --git a/ModVersionChecker/data/model/CheckerTypeDef.cs b/ModVersionChecker/data/model/CheckerTypeDef.cs deleted file mode 100644 index 3003d98..0000000 --- a/ModVersionChecker/data/model/CheckerTypeDef.cs +++ /dev/null @@ -1,18 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Text.Json.Serialization; -using System.Threading.Tasks; - -namespace ModVersionChecker.data.model -{ - public class CheckerTypeDef - { - [JsonPropertyName("name")] - public string Name { get; set; } = string.Empty; - - [JsonPropertyName("params")] - public List Params { get; set; } = new List(); - } -} diff --git a/ModVersionChecker/data/model/FsModPathConfig.cs b/ModVersionChecker/data/model/FsModPathConfig.cs deleted file mode 100644 index ecf8946..0000000 --- a/ModVersionChecker/data/model/FsModPathConfig.cs +++ /dev/null @@ -1,36 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Text.Json.Serialization; -using System.Threading.Tasks; - -namespace ModVersionChecker.data.model -{ - public class FsModPathConfig - { - - public string Id { get; set; } = String.Empty; - - [JsonPropertyName("name")] - public string Name { get; set; } = string.Empty; - - [JsonPropertyName("shortName")] - public string ShortName { get; set; } = string.Empty; - - [JsonPropertyName("path")] - public string Path { get; set; } = string.Empty; - - [JsonPropertyName("file")] - public string File { get; set; } = string.Empty; - - [JsonPropertyName("fileType")] - public string FileType { get; set; } = string.Empty; - - [JsonPropertyName("key")] - public string Key { get; set; } = string.Empty; - - [JsonPropertyName("fields")] - public List Fields { get; set; } = new List(); - } -} diff --git a/ModVersionChecker/data/sourcesDef.json b/ModVersionChecker/data/sourcesDef.json deleted file mode 100644 index 39cd3cb..0000000 --- a/ModVersionChecker/data/sourcesDef.json +++ /dev/null @@ -1,47 +0,0 @@ - [ - { - "id": "custom", - "name": "Custom Source", - "type": "scrape", - "defaults": { - "regex": "", - "url": "" - } - }, - { - "id": "sim_market", - "name": "Sim Market", - "type": "scrape", - "defaults": { - "regex": "(\\d+\\.\\d+\\.\\d+)<\\/span>", - "url": "https://secure.simmarket.com/" - } - }, - { - "id": "pms50_gtn750", - "name": "PMS50 GTN750", - "type": "scrape", - "defaults": { - "url": "https://pms50.com/msfs/", - "regex": "Current version: (\\d+\\.\\d+\\.\\d+)" - } - }, - { - "id": "sws", - "name": "SWS", - "type": "scrape", - "defaults": { - "url": "https://simworksstudios.com/product", - "regex": "Current Version: (\\d+\\.\\d+\\.\\d+)" - } - }, - { - "id": "a2a", - "name": "A2A", - "type": "scrape", - "defaults": { - "url": "https://a2asimulations.com/forum/viewtopic.php?f=153", - "mode": "selenium" - } - } - ] \ No newline at end of file diff --git a/ModVersionChecker/data/model/AppStatus.cs b/ModVersionChecker/enums/AppStatus.cs similarity index 63% rename from ModVersionChecker/data/model/AppStatus.cs rename to ModVersionChecker/enums/AppStatus.cs index 8e2481d..b1bc574 100644 --- a/ModVersionChecker/data/model/AppStatus.cs +++ b/ModVersionChecker/enums/AppStatus.cs @@ -4,12 +4,12 @@ using System.Linq; using System.Text; using System.Threading.Tasks; -namespace ModVersionChecker.data.model +namespace ModVersionChecker.enums { public enum AppStatus { - None, - UpdateAvailable, - Error, + NONE, + UPDATE_AVAILABLE, + ERROR, } } diff --git a/ModVersionChecker/forms/AppDetailsForm.cs b/ModVersionChecker/forms/AppDetailsForm.cs deleted file mode 100644 index 637ed78..0000000 --- a/ModVersionChecker/forms/AppDetailsForm.cs +++ /dev/null @@ -1,477 +0,0 @@ -using Microsoft.VisualBasic.FileIO; -using ModVersionChecker.data.model; -using ModVersionChecker.managers.interfaces; -using System; -using System.Collections.Generic; -using System.Linq; -using System.Windows.Forms; -using static System.Windows.Forms.VisualStyles.VisualStyleElement.Window; - -namespace ModVersionChecker.forms -{ - public class AppDetailsForm : Form - { - private readonly IConfigManager _configManager; - private readonly IAppsManager _appsManager; - private readonly ISourcesDefManager _sourcesDefManager; - private readonly ICheckerTypesDefManager _checkerTypesDefManager; - private readonly IFlightSimsManager _flightSimsManager; - private readonly GlobalConfig _globalConfig; - private int _currentRow; - //private string? _appId; - private bool _isEditable; - //private List _apps; - private List _sourcesDef; - private List _checkerTypesDef; - - private TextBox _nameField, _downloadUrlField; - private Label _nameLabel, _msfsVersionsLabel, _sourceLabel, _paramsSubtitle, _downloadUrlLabel; - private ComboBox _sourceField; - private Button _saveButton, _closeButton; - private TableLayoutPanel _mainLayout, _paramsPanel, _fsFieldsPanel; - private FlowLayoutPanel _buttonsPanel, _fsPanel; - private readonly Dictionary _paramFields = new Dictionary(); - private readonly Dictionary> _fsFields = new Dictionary>(); - private List _selectedFs = new List(); - private List _fsCheckBoxes = new List(); - private AppConfig? _currentApp; - - private List _flightSims; - - public event EventHandler OnAppChanged; - - public AppDetailsForm( - IConfigManager configManager, - IAppsManager appsManager, - ISourcesDefManager sourcesDefManager, - ICheckerTypesDefManager checkerTypesDefManager, - IFlightSimsManager flightSimsManager - ) - { - - _configManager = configManager ?? throw new ArgumentNullException(nameof(configManager)); - _appsManager = appsManager ?? throw new ArgumentNullException(nameof(appsManager)); - _sourcesDefManager = sourcesDefManager ?? throw new ArgumentNullException(nameof(sourcesDefManager)); - _checkerTypesDefManager = checkerTypesDefManager ?? throw new ArgumentNullException(nameof(checkerTypesDefManager)); - _flightSimsManager = flightSimsManager ?? throw new ArgumentNullException(nameof(flightSimsManager)); - - _flightSims = _flightSimsManager.Load() ?? new List(); - - _globalConfig = _configManager.Load() ?? new GlobalConfig(); - - _sourcesDef = _sourcesDefManager.List() ?? new List(); - _checkerTypesDef = _checkerTypesDefManager.Load() ?? new List(); - - - _selectedFs = _flightSims.Select(sim => sim.ShortName).ToList(); - - _mainLayout = new TableLayoutPanel - { - Dock = DockStyle.Fill, - ColumnCount = 2, - RowCount = 14, - ColumnStyles = { new ColumnStyle(SizeType.Absolute, 150), new ColumnStyle(SizeType.Percent, 100) } - }; - - // App Name - _nameLabel = new Label { Text = "Name:" }; - _nameField = new TextBox { Text = "", Enabled = _isEditable, Width = 300 }; - // FS Versions - _msfsVersionsLabel = new Label { Text = "FS:" }; - _fsPanel = new FlowLayoutPanel - { - FlowDirection = FlowDirection.LeftToRight, - AutoSize = true, - Dock = DockStyle.Fill - }; - //_msfs2020CheckBox = new CheckBox { Text = "MSFS 2020", Enabled = _isEditable }; - //_msfs2024CheckBox = new CheckBox { Text = "MSFS 2024", Enabled = _isEditable }; - // Source - _sourceLabel = new Label { Text = "Source:" }; - _sourceField = new ComboBox { Enabled = _isEditable, Width = 300, DropDownStyle = ComboBoxStyle.DropDownList }; - _sourceField.Items.AddRange(_sourcesDef.Select(sd => sd.Id).ToArray()); - _sourceField.SelectedIndexChanged += OnSourceFieldIndexChanged; - // Parameters - _paramsSubtitle = new Label { Text = "SourceParameters:", Font = new System.Drawing.Font(Font, System.Drawing.FontStyle.Bold) }; - _paramsPanel = new TableLayoutPanel - { - AutoSize = true, - BackColor = Color.White, - Dock = DockStyle.Fill, - ColumnCount = 2, - RowCount = 2, - ColumnStyles = { new ColumnStyle(SizeType.Absolute, 150), new ColumnStyle(SizeType.Percent, 100) } - }; - // Fs Fields Panel - _fsFieldsPanel = new TableLayoutPanel - { - AutoSize = true, - BackColor = Color.White, - Dock = DockStyle.Fill, - ColumnCount = 2, - RowCount = 2, - ColumnStyles = { new ColumnStyle(SizeType.Absolute, 150), new ColumnStyle(SizeType.Percent, 100) } - }; - - - // App Name - _downloadUrlLabel = new Label { Text = "Download Url:" }; - _downloadUrlField = new TextBox { Text = "", Enabled = _isEditable, Width = 300 }; - - _buttonsPanel = new FlowLayoutPanel { FlowDirection = FlowDirection.RightToLeft, AutoSize = true, Dock = DockStyle.Fill }; - _saveButton = new Button { Text = "Save", Width = 100 }; - _closeButton = new Button { Text = "Close", Width = 100 }; - _saveButton.Click += OnSaveButtonClicked; - _closeButton.Click += (s, e) => Close(); - Controls.Add(_mainLayout); - - Size = new System.Drawing.Size(500, 500); - StartPosition = FormStartPosition.CenterParent; - - InitializeForm(); - - } - - public void SetApp(AppConfig? app, bool update = true) - { - _currentApp = app; - _selectedFs = _currentApp?.MsfsVersions ?? new List(); - - if (update) - { - UpdateForm(); - } - } - - public void SetEditable(bool isEditable, bool update = true) - { - _isEditable = isEditable; - if (update) - { - UpdateForm(); - } - } - - public void UpdateForm() - { - - Text = _currentApp == null ? "Add App" : (_isEditable ? "Edit App" : "App Details"); - - _nameField.Text = _currentApp != null ? _currentApp.Name : ""; - _downloadUrlField.Enabled = _nameField.Enabled = _sourceField.Enabled = _isEditable; - _downloadUrlField.Text = _currentApp != null ? _currentApp.DownloadUrl : ""; - - _flightSims.ForEach(fs => - { - if (_currentApp != null && _currentApp.MsfsVersions.Contains(fs.ShortName)) - { - if (!_selectedFs.Contains(fs.ShortName)) - { - _selectedFs.Add(fs.ShortName); - } - } - }); - - for (int i = 0; i < _fsCheckBoxes.Count; i++) - { - var fsKey = _flightSims.FirstOrDefault(f => f.ShortName == _fsCheckBoxes[i].Text)?.ShortName; - if (fsKey != null) - { - _fsCheckBoxes[i].Checked = _currentApp != null && _currentApp.MsfsVersions.Contains(fsKey); - } - } - - _sourceField.SelectedIndex = _sourceField.Items.IndexOf(_currentApp != null ? _currentApp.Source : ""); - - UpdateFsFields(); - - UpdateParamFields(); - } - - private bool isFsSelected(FsModPathConfig fs) - { - return _selectedFs.Contains(fs.ShortName); - } - private void UpdateFsFields() - { - _fsFields.Clear(); - _fsFieldsPanel.Controls.Clear(); - - foreach (var fs in _flightSims) - { - - if (fs == null || !isFsSelected(fs)) - { - continue; - } - - var fsKey = fs.ShortName; - var fieldsDict = new Dictionary(); - _fsFields[fsKey] = fieldsDict; - int currentRow = 0; - - Label horizontalSeparator = new Label - { - Height = 50, - Padding = new Padding(10, 0, 0, 0), - BackColor = Color.GhostWhite, // Line-like separator - Dock = DockStyle.Fill, - TextAlign = ContentAlignment.MiddleLeft, - Text = fsKey, // Optional: Add text to the separator - ForeColor = Color.FromArgb(50, 50, 50) // Text color contrasts with background - }; - - _fsFieldsPanel.Controls.Add(horizontalSeparator, 0, currentRow); - _fsFieldsPanel.SetColumnSpan(horizontalSeparator, 2); - currentRow++; - - - foreach (var field in fs.Fields) - { - Control control; - var value = GetFsFieldValue(fsKey, field.Name); - var label = new Label { Text = $"{field.Label} ({(field.Required ? "Required" : "Optional")}):", Width = 100, AutoSize = true }; - var textBox = new TextBox - { - Width = 300, - Enabled = _isEditable, - Text = value - }; - - switch (field.Control.ToLower()) - { - case "directory": - textBox.ReadOnly = true; - control = new TableLayoutPanel - { - AutoSize = true, - Dock = DockStyle.Fill, - ColumnCount = 2, - RowCount = 1, - ColumnStyles = { new ColumnStyle(SizeType.Percent, 80), new ColumnStyle(SizeType.Percent, 20) } - }; - (control as TableLayoutPanel).Controls.Add(textBox, 0, 0); - var browseButton = new Button { Text = "Browse", Width = 80, Enabled = _isEditable }; - browseButton.Click += (s, e) => - { - using (var folderDialog = new FolderBrowserDialog()) - { - folderDialog.Description = $"Select directory for {field.Label}"; - folderDialog.SelectedPath = textBox.Text == "" ? Path.Combine(fs.Path) : textBox.Text; - - if (folderDialog.ShowDialog() == DialogResult.OK) - { - string selectedDirectory = folderDialog.SelectedPath; - string folderName = Path.GetFileName(selectedDirectory); - textBox.Text = folderName; - } - } - }; - (control as TableLayoutPanel).Controls.Add(browseButton, 1, 0); - break; - default: - control = textBox; - break; - } - - fieldsDict[field.Name] = textBox; - - _fsFieldsPanel.Controls.Add(label, 0, currentRow); - _fsFieldsPanel.Controls.Add(control, 1, currentRow); - currentRow++; - } - } - } - - private string GetFsFieldValue(string fsKey, string fieldName) - { - if (_currentApp == null) return ""; - - var fsFields = _currentApp.FsFields.ContainsKey(fsKey) ? _currentApp.FsFields[fsKey] : new Dictionary(); - if (fsFields.ContainsKey(fieldName)) - { - return fsFields[fieldName]; - } - return ""; - } - - private void UpdateParamFields() - { - if (_sourceField?.SelectedItem == null) return; - - var selectedSource = _sourcesDef.FirstOrDefault(sd => sd.Id == _sourceField.SelectedItem.ToString()); - if (selectedSource == null) return; - - var checkerType = _checkerTypesDef.FirstOrDefault(ct => ct.Name == selectedSource.Type); - if (checkerType == null) return; - - _paramFields.Clear(); - _paramsPanel.Controls.Clear(); - - int currentRow = 0; - foreach (var paramDef in checkerType.Params) - { - var label = new Label { Text = $"{paramDef.Label} ({(paramDef.Required ? "Required" : "Optional")}):", Width = 100, AutoSize = true }; - var textBox = new TextBox - { - Width = 300, - Enabled = _isEditable, - Text = GetParamValue(paramDef.Name, selectedSource) - }; - _paramFields[paramDef.Name] = textBox; - - _paramsPanel.Controls.Add(label, 0, currentRow); - _paramsPanel.Controls.Add(textBox, 1, currentRow); - currentRow++; - } - } - - private string GetParamValue(string paramName, SourceDef source) - { - var valueFromSource = source.Defaults != null && source.Defaults.ContainsKey(paramName) ? source.Defaults[paramName] : ""; - - - if (_currentApp == null || _currentApp.Params == null || !_currentApp.Params.ContainsKey(paramName)) - return valueFromSource; - return _currentApp.Params[paramName]; - } - - - private void InitializeForm() - { - - - _currentRow = 0; - _mainLayout.Controls.Add(_nameLabel, 0, _currentRow); - _mainLayout.Controls.Add(_nameField, 1, _currentRow++); - _mainLayout.Controls.Add(_msfsVersionsLabel, 0, _currentRow++); - _mainLayout.Controls.Add(_fsPanel, 1, _currentRow); - _mainLayout.SetColumnSpan(_fsPanel, 2); - _currentRow++; - _mainLayout.Controls.Add(_fsFieldsPanel, 0, _currentRow); - _mainLayout.SetColumnSpan(_fsFieldsPanel, 2); - _currentRow++; - - _mainLayout.Controls.Add(_sourceLabel, 0, _currentRow); - _mainLayout.Controls.Add(_sourceField, 1, _currentRow++); - _mainLayout.Controls.Add(_paramsSubtitle, 0, _currentRow); - _mainLayout.SetColumnSpan(_paramsSubtitle, 2); - _currentRow++; - _mainLayout.Controls.Add(_paramsPanel, 0, _currentRow); - _mainLayout.SetColumnSpan(_paramsPanel, 2); - _currentRow++; - _mainLayout.Controls.Add(_downloadUrlLabel, 0, _currentRow); - _mainLayout.Controls.Add(_downloadUrlField, 1, _currentRow++); - _currentRow++; - - - _mainLayout.Controls.Add(_buttonsPanel, 0, _currentRow++); - - AddFsCheckboxes(); - AddButtons(); - - // UpdateForm(); - } - - private void AddFsCheckboxes() - { - foreach (var fs in _flightSims) - { - var checkBox = new CheckBox - { - Text = fs.ShortName, - Checked = _currentApp != null && _currentApp.MsfsVersions.Contains(fs.ShortName), - }; - checkBox.CheckedChanged += (s, e) => - { - if (checkBox.Checked) - { - if (!_selectedFs.Contains(fs.ShortName)) - { - _selectedFs.Add(fs.ShortName); - } - } - else - { - _selectedFs.Remove(fs.ShortName); - } - UpdateFsFields(); - }; - _fsPanel.Controls.Add(checkBox); - _fsCheckBoxes.Add(checkBox); - } - } - - - private void OnSourceFieldIndexChanged(object? sender, EventArgs e) - { - if (_isEditable && _sourceField.SelectedItem != null) - { - UpdateParamFields(); - } - } - - private void AddButtons() - { - _buttonsPanel.Controls.Clear(); - _buttonsPanel.Controls.Add(_saveButton); - _buttonsPanel.Controls.Add(_closeButton); - } - - private void OnSaveButtonClicked(object? sender, EventArgs e) - { - try { - var paramsDict = _paramFields.ToDictionary(kvp => kvp.Key, kvp => kvp.Value.Text.Trim()); - var fsFieldsDict = _fsFields.ToDictionary( - kvp => kvp.Key, - kvp => kvp.Value.ToDictionary(fkvp => fkvp.Key, fkvp => fkvp.Value.Text.Trim()) - ); - var requiredParams = _checkerTypesDef - .First(ct => ct.Name == _sourcesDef.FirstOrDefault(sd => sd.Id == _sourceField.SelectedItem?.ToString())?.Type) - .Params.Where(p => p.Required) - .Select(p => p.Name); - if (requiredParams.Any(rp => string.IsNullOrWhiteSpace(paramsDict[rp]))) - { - throw new Exception("All required parameters must be filled."); - } - var msfsVersions = _selectedFs; - var isNewApp = (_currentApp == null || string.IsNullOrEmpty(_currentApp.Id)); - var app = new AppConfig - { - Id = isNewApp ? GetUuid() : _currentApp.Id, - Name = _nameField.Text.Trim(), - MsfsVersions = msfsVersions, - Source = _sourceField.SelectedItem?.ToString() ?? "", - Params = paramsDict, - FsFields = fsFieldsDict, - DownloadUrl = _downloadUrlField.Text.Trim(), - CurrentVersion = _currentApp?.CurrentVersion ?? "", - LatestVersion = _currentApp?.LatestVersion ?? "", - Status = _currentApp?.Status ?? AppStatus.None - }; - - if (isNewApp) - { - _appsManager.Insert(app); - } else - { - _appsManager.Update(app); - } - - _currentApp = app; - OnAppChanged?.Invoke(this, "App saved"); - Close(); - } - catch (Exception ex) - { - MessageBox.Show($"Error: {ex.Message}", "Invalid Input", MessageBoxButtons.OK, MessageBoxIcon.Error); - } - } - - private string GetUuid() - { - Guid uuid = Guid.NewGuid(); - return uuid.ToString(); - } - } -} \ No newline at end of file diff --git a/ModVersionChecker/forms/FormFactory.cs b/ModVersionChecker/forms/FormFactory.cs deleted file mode 100644 index 29ed561..0000000 --- a/ModVersionChecker/forms/FormFactory.cs +++ /dev/null @@ -1,68 +0,0 @@ -using Microsoft.Extensions.DependencyInjection; -using ModVersionChecker.data.model; -using ModVersionChecker.managers.interfaces; - -namespace ModVersionChecker.forms -{ - public class FormFactory : IFormFactory - { - private readonly IServiceProvider _serviceProvider; - - public FormFactory(IServiceProvider serviceProvider) - { - _serviceProvider = serviceProvider ?? throw new ArgumentNullException(nameof(serviceProvider)); - } - - public AppDetailsForm CreateAppDetailsForm(AppConfig? app, bool isEditable, EventHandler? onAppChanged) - { - var configManager = _serviceProvider.GetRequiredService(); - var appsManager = _serviceProvider.GetRequiredService(); - var sourcesDefManager = _serviceProvider.GetRequiredService(); - var checkerTypesDefManager = _serviceProvider.GetRequiredService(); - var flightSimsManager = _serviceProvider.GetRequiredService(); - var form = new AppDetailsForm(configManager, appsManager, sourcesDefManager, checkerTypesDefManager, flightSimsManager); - form.SetApp(app, false); - form.SetEditable(isEditable); - if (onAppChanged != null) - { - form.OnAppChanged += onAppChanged; - } - return form; - } - - public GlobalConfigForm CreateGlobalConfigForm() - { - var configManager = _serviceProvider.GetRequiredService(); - var form = new GlobalConfigForm(configManager); - return form; - } - - public SourcesConfigForm CreateSourcesConfigForm(EventHandler? onSourcesChanged) - { - var sourcesDefManager = _serviceProvider.GetRequiredService(); - var formFactory = _serviceProvider.GetRequiredService(); - var form = new SourcesConfigForm(formFactory, sourcesDefManager); - if (onSourcesChanged != null) - { - form.OnSourcesChanged += onSourcesChanged; - } - return form; - } - - public SourceDetailForm CreateSourceDetailForm(SourceDef? sourceDef, EventHandler? onSourceChanged) - { - var sourcesDefManager = _serviceProvider.GetRequiredService(); - var checkerTypesDefManager = _serviceProvider.GetRequiredService(); - var formFactory = _serviceProvider.GetRequiredService(); - var form = new SourceDetailForm(formFactory, sourcesDefManager); - form.SourceDef = sourceDef; - - if (onSourceChanged != null) - { - form.UpdateFields(); - form.OnSourceChanged += onSourceChanged; - } - return form; - } - } -} \ No newline at end of file diff --git a/ModVersionChecker/forms/IFormFactory.cs b/ModVersionChecker/forms/IFormFactory.cs deleted file mode 100644 index 78637ee..0000000 --- a/ModVersionChecker/forms/IFormFactory.cs +++ /dev/null @@ -1,14 +0,0 @@ -using ModVersionChecker.data.model; - -namespace ModVersionChecker.forms -{ - public interface IFormFactory - { - AppDetailsForm CreateAppDetailsForm(AppConfig? app, bool isEditable, EventHandler? onAppChanged); - GlobalConfigForm CreateGlobalConfigForm(); - - SourcesConfigForm CreateSourcesConfigForm(EventHandler? onSourcesChanged); - - SourceDetailForm CreateSourceDetailForm(SourceDef? sourceDef, EventHandler? onSourceChanged); - } -} \ No newline at end of file diff --git a/ModVersionChecker/forms/SourceDetailForm.cs b/ModVersionChecker/forms/SourceDetailForm.cs deleted file mode 100644 index 7d04a20..0000000 --- a/ModVersionChecker/forms/SourceDetailForm.cs +++ /dev/null @@ -1,110 +0,0 @@ -using ModVersionChecker.data.model; -using ModVersionChecker.managers.interfaces; -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; - -namespace ModVersionChecker.forms -{ - // Simple editor form for SourceDef - public class SourceDetailForm : Form - { - private readonly IFormFactory _formFactory; - private readonly ISourcesDefManager _sourceManager; - public SourceDef SourceDef { get; set; } - public Boolean IsEditable => !string.IsNullOrWhiteSpace(SourceDef?.Id); - private TextBox _idField, _nameField, _typeField, _defaultsField; - private Button _okButton, _cancelButton; - - public event EventHandler? OnSourceChanged; - - public SourceDetailForm(IFormFactory formFactory, ISourcesDefManager sourceManager) - { - _formFactory = formFactory ?? throw new ArgumentNullException(nameof(formFactory)); - _sourceManager = sourceManager ?? throw new ArgumentNullException(nameof(sourceManager)); - - InitializeComponent(); - _formFactory = formFactory; - } - - private void InitializeComponent() - { - Text = "Edit SourceDef"; - Size = new Size(400, 300); - StartPosition = FormStartPosition.CenterParent; - Padding = new Padding(10); - - var layout = new TableLayoutPanel - { - Dock = DockStyle.Fill, - RowCount = 5, - ColumnCount = 2, - Padding = new Padding(10) - }; - - layout.Controls.Add(new Label { Text = "Id:", Width = 80 }, 0, 0); - _idField = new TextBox { Text = "", Width = 200 }; - layout.Controls.Add(_idField, 1, 0); - - layout.Controls.Add(new Label { Text = "Name:", Width = 80 }, 0, 1); - _nameField = new TextBox { Text = "", Width = 200 }; - layout.Controls.Add(_nameField, 1, 1); - - layout.Controls.Add(new Label { Text = "Type:", Width = 80 }, 0, 2); - _typeField = new TextBox { Text = "", Width = 200 }; - layout.Controls.Add(_typeField, 1, 2); - - layout.Controls.Add(new Label { Text = "Defaults (key=value, comma separated):", Width = 80 }, 0, 3); - _defaultsField = new TextBox { Text = "", Width = 200 }; - layout.Controls.Add(_defaultsField, 1, 3); - - var buttonPanel = new FlowLayoutPanel { FlowDirection = FlowDirection.RightToLeft, Dock = DockStyle.Fill }; - _okButton = new Button { Text = "OK", DialogResult = DialogResult.OK }; - _cancelButton = new Button { Text = "Cancel", DialogResult = DialogResult.Cancel }; - buttonPanel.Controls.Add(_okButton); - buttonPanel.Controls.Add(_cancelButton); - - layout.Controls.Add(buttonPanel, 0, 4); - layout.SetColumnSpan(buttonPanel, 2); - - Controls.Add(layout); - - _okButton.Click += (s, e) => - { - SourceDef.Id = _idField.Text.Trim(); - SourceDef.Name = _nameField.Text.Trim(); - SourceDef.Type = _typeField.Text.Trim(); - SourceDef.Defaults = ParseDefaults(_defaultsField.Text); - DialogResult = DialogResult.OK; - Close(); - }; - _cancelButton.Click += (s, e) => { DialogResult = DialogResult.Cancel; Close(); }; - } - - public void UpdateFields() - { - if (SourceDef != null) - { - _idField.Text = SourceDef.Id; - _nameField.Text = SourceDef.Name; - _typeField.Text = SourceDef.Type; - _defaultsField.Text = string.Join(", ", SourceDef.Defaults.Select(d => $"{d.Key}={d.Value}")); - } - } - - private Dictionary ParseDefaults(string text) - { - var dict = new Dictionary(); - var pairs = text.Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries); - foreach (var pair in pairs) - { - var kv = pair.Split(new[] { '=' }, 2); - if (kv.Length == 2) - dict[kv[0].Trim()] = kv[1].Trim(); - } - return dict; - } - } -} diff --git a/ModVersionChecker/forms/SourcesConfigForm.cs b/ModVersionChecker/forms/SourcesConfigForm.cs deleted file mode 100644 index 0b23cfa..0000000 --- a/ModVersionChecker/forms/SourcesConfigForm.cs +++ /dev/null @@ -1,136 +0,0 @@ -using ModVersionChecker.data.model; -using ModVersionChecker.managers.interfaces; -using System; -using System.Collections.Generic; -using System.Drawing; -using System.Linq; -using System.Windows.Forms; - -namespace ModVersionChecker.forms -{ - public class SourcesConfigForm : Form - { - private List _sourceDefs; - private ListView _listView; - private Button _addButton, _editButton, _deleteButton, _closeButton; - private TableLayoutPanel _mainLayout; - private readonly ISourcesDefManager _sourcesManager; - private readonly IFormFactory _formFactory; - public event EventHandler? OnSourcesChanged; - public List SourceDefs => _sourceDefs; - - public SourcesConfigForm(IFormFactory formFactory, ISourcesDefManager sourcesManager) - { - _sourcesManager = sourcesManager ?? throw new ArgumentNullException(nameof(sourcesManager)); - _formFactory = formFactory ?? throw new ArgumentNullException(nameof(formFactory)); - _sourceDefs = _sourcesManager.List() ?? new List(); - Padding = new Padding(20); - InitializeComponent(); - } - - private void InitializeComponent() - { - Text = "Source Definitions"; - Size = new Size(800, 400); - StartPosition = FormStartPosition.CenterParent; - - _mainLayout = new TableLayoutPanel - { - Dock = DockStyle.Fill, - RowCount = 2, - ColumnCount = 1, - Padding = new Padding(10) - }; - _mainLayout.RowStyles.Add(new RowStyle(SizeType.Percent, 80)); - _mainLayout.RowStyles.Add(new RowStyle(SizeType.Percent, 20)); - - _listView = new ListView - { - Dock = DockStyle.Fill, - View = View.Details, - FullRowSelect = true, - MultiSelect = false, - GridLines = true - }; - _listView.Columns.Add("Id", 100); - _listView.Columns.Add("Name", 150); - _listView.Columns.Add("Type", 100); - _listView.Columns.Add("Defaults", -2); - - UpdateListView(); - - _mainLayout.Controls.Add(_listView, 0, 0); - - var buttonPanel = new FlowLayoutPanel { Dock = DockStyle.Fill, FlowDirection = FlowDirection.LeftToRight }; - _addButton = new Button { Text = "Add" }; - _editButton = new Button { Text = "Edit", Enabled = false }; - _deleteButton = new Button { Text = "Delete", Enabled = false }; - _closeButton = new Button { Text = "Close", DialogResult = DialogResult.OK }; - - _addButton.Click += (s, e) => AddSourceDef(); - _editButton.Click += (s, e) => EditSourceDef(); - _deleteButton.Click += (s, e) => DeleteSourceDef(); - _closeButton.Click += (s, e) => Close(); - - _listView.SelectedIndexChanged += (s, e) => - { - bool hasSelection = _listView.SelectedItems.Count > 0; - _editButton.Enabled = hasSelection; - _deleteButton.Enabled = hasSelection; - }; - - buttonPanel.Controls.AddRange(new Control[] { _addButton, _editButton, _deleteButton, _closeButton }); - _mainLayout.Controls.Add(buttonPanel, 0, 1); - - Controls.Add(_mainLayout); - } - - private void UpdateListView() - { - _listView.Items.Clear(); - foreach (var src in _sourceDefs) - { - var item = new ListViewItem(src.Id); - item.SubItems.Add(src.Name); - item.SubItems.Add(src.Type); - item.SubItems.Add(string.Join(", ", src.Defaults.Select(d => $"{d.Key}={d.Value}"))); - item.Tag = src; - _listView.Items.Add(item); - } - } - - private void AddSourceDef() - { - EventHandler? handler = (s, e) => MessageBox.Show("Source Changed"); - var editor = _formFactory.CreateSourceDetailForm(null, handler); - if (editor.ShowDialog() == DialogResult.OK) - { - _sourceDefs.Add(editor.SourceDef); - UpdateListView(); - } - } - - private void EditSourceDef() - { - if (_listView.SelectedItems.Count == 0) return; - var src = _listView.SelectedItems[0].Tag as SourceDef; - EventHandler? handler = (s, e) => MessageBox.Show("Source Changed"); - var editor = _formFactory.CreateSourceDetailForm(src, handler); - - if (editor.ShowDialog() == DialogResult.OK) - { - //int idx = _sourceDefs.IndexOf(src); - //_sourceDefs[idx] = editor.SourceDef; - //UpdateListView(); - } - } - - private void DeleteSourceDef() - { - if (_listView.SelectedItems.Count == 0) return; - var src = _listView.SelectedItems[0].Tag as SourceDef; - _sourceDefs.Remove(src); - UpdateListView(); - } - } -} \ No newline at end of file diff --git a/ModVersionChecker/managers/filesystem/CheckerTypesDefManager.cs b/ModVersionChecker/managers/filesystem/CheckerTypesDefManager.cs deleted file mode 100644 index 7f51b80..0000000 --- a/ModVersionChecker/managers/filesystem/CheckerTypesDefManager.cs +++ /dev/null @@ -1,28 +0,0 @@ -using ModVersionChecker.data.model; -using ModVersionChecker.managers.interfaces; -using System.Collections.Generic; -using System.IO; -using System.Text.Json; - -namespace ModVersionChecker.managers.filesystem -{ - public class CheckerTypesDefManager : ICheckerTypesDefManager - { - private readonly string FilePath = Path.Combine(AppContext.BaseDirectory, "data", "checkerTypesDef.json"); - - public List Load() - { - if (!File.Exists(FilePath)) - return new List(); - var json = File.ReadAllText(FilePath); - return JsonSerializer.Deserialize>(json) ?? new(); - } - - public void Save(List checkerTypesDef) - { - var options = new JsonSerializerOptions { WriteIndented = true }; - var json = JsonSerializer.Serialize(checkerTypesDef, options); - File.WriteAllText(FilePath, json); - } - } -} \ No newline at end of file diff --git a/ModVersionChecker/managers/interfaces/ICheckerTypesDefManager.cs b/ModVersionChecker/managers/interfaces/ICheckerTypesDefManager.cs deleted file mode 100644 index 7e93700..0000000 --- a/ModVersionChecker/managers/interfaces/ICheckerTypesDefManager.cs +++ /dev/null @@ -1,15 +0,0 @@ -using ModVersionChecker.data.model; -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; - -namespace ModVersionChecker.managers.interfaces -{ - public interface ICheckerTypesDefManager - { - List Load(); - void Save(List checkerTypesDef); - } -} diff --git a/ModVersionChecker/managers/litedb/AppConfigLiteDb.cs b/ModVersionChecker/managers/litedb/AppConfigLiteDb.cs deleted file mode 100644 index 72965a0..0000000 --- a/ModVersionChecker/managers/litedb/AppConfigLiteDb.cs +++ /dev/null @@ -1,70 +0,0 @@ -using ModVersionChecker.data.model; -using ModVersionChecker.managers.interfaces; -using ModVersionChecker.utils; -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; - -namespace ModVersionChecker.managers.litedb -{ - public class AppConfigLiteDb : LiteDb, IAppsManager - { - private string collection = LiteDb.APPS_COLLECTION; - public List Load() - { - var col = _db.GetCollection(collection); - return col.FindAll().ToList(); - } - - public void Insert(AppConfig app) - { - var now = TimeUtils.GetUnixTimeMillis(null); - app.CreatedAt = now; - app.UpdatedAt = now; - var col = _db.GetCollection(collection); - col.Insert(app); - } - - public void Update(AppConfig app) - { - var now = TimeUtils.GetUnixTimeMillis(null); - app.UpdatedAt = now; - var col = _db.GetCollection(collection); - col.Update(app); - } - - //public void Upsert(AppConfig app) - //{ - // var now = TimeUtils.GetUnixTimeMillis(null); - // app.UpdatedAt = now; - // var col = _db.GetCollection(collection); - // if (string.IsNullOrEmpty(app.Id)) - // { - // app.CreatedAt = now; - // col.Insert(app); - // } - // col.Update(app); - //} - - public void Delete(string id) - { - var col = _db.GetCollection(collection); - col.Delete(id); - } - - public void Save(List apps) - { - - } - - public void UpdateStatus(AppConfig app, AppStatus status) - { - app.LastCheckedAt = TimeUtils.GetUnixTimeMillis(null); - app.Status = status; - var col = _db.GetCollection(collection); - col.Update(app); - } - } -} diff --git a/ModVersionChecker/managers/litedb/ConfigLiteDb.cs b/ModVersionChecker/managers/litedb/ConfigLiteDb.cs deleted file mode 100644 index 46dd6ec..0000000 --- a/ModVersionChecker/managers/litedb/ConfigLiteDb.cs +++ /dev/null @@ -1,24 +0,0 @@ -using ModVersionChecker.data.model; -using ModVersionChecker.managers.interfaces; - -namespace ModVersionChecker.managers.litedb -{ - public class ConfigLiteDb : LiteDb, IConfigManager - { - private string collection = LiteDb.CONFIG_COLLECTION; - public GlobalConfig Load() - { - var col = _db.GetCollection(collection); - return col.FindAll().FirstOrDefault() ?? new GlobalConfig(); - } - public void Save(GlobalConfig config) - { - var col = _db.GetCollection(collection); - col.Upsert(config); - } - public GlobalConfig GetConfig() - { - return Load(); - } - } -} diff --git a/ModVersionChecker/managers/litedb/FlightSimsLiteDb.cs b/ModVersionChecker/managers/litedb/FlightSimsLiteDb.cs deleted file mode 100644 index 9c56e1b..0000000 --- a/ModVersionChecker/managers/litedb/FlightSimsLiteDb.cs +++ /dev/null @@ -1,32 +0,0 @@ -using ModVersionChecker.data.model; -using ModVersionChecker.managers.interfaces; -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; - -namespace ModVersionChecker.managers.litedb -{ - internal class FlightSimsLiteDb : LiteDb, IFlightSimsManager - { - private string collection = FLIGHT_SIMS_COLLECTION; - public List Load() - { - var col = _db.GetCollection(collection); - return col.FindAll().ToList(); - } - - public void Save(FsModPathConfig config) - { - var col = _db.GetCollection(collection); - col.Upsert(config); - } - - public FsModPathConfig? GetByShortName(string id) - { - var col = _db.GetCollection(collection); - return col.FindOne(x => x.ShortName == id); - } - } -} diff --git a/ModVersionChecker/managers/litedb/SourcesLiteDb.cs b/ModVersionChecker/managers/litedb/SourcesLiteDb.cs deleted file mode 100644 index 81ee66a..0000000 --- a/ModVersionChecker/managers/litedb/SourcesLiteDb.cs +++ /dev/null @@ -1,43 +0,0 @@ -using ModVersionChecker.data.model; -using ModVersionChecker.managers.interfaces; -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; - -namespace ModVersionChecker.managers.litedb -{ - public class SourcesLiteDb : LiteDb, ISourcesDefManager - { - private string collection = SOURCES_DEF_COLLECTION; - public List List() - { - var col = _db.GetCollection(collection); - return col.FindAll().ToList(); - } - - public SourceDef? GetById(string id) - { - var col = _db.GetCollection(collection); - return col.FindOne(x => x.Id == id); - } - - public void AddSourceDef(SourceDef sourceDef) - { - var col = _db.GetCollection(collection); - col.Insert(sourceDef); - } - public void RemoveSourceDef(string id) - { - var col = _db.GetCollection(collection); - col.Delete(id); - } - - public void Save(SourceDef sourceDef) - { - var col = _db.GetCollection(collection); - col.Upsert(sourceDef); - } - } -} diff --git a/ModVersionChecker/data/model/AppConfig.cs b/ModVersionChecker/model/App.cs similarity index 54% rename from ModVersionChecker/data/model/AppConfig.cs rename to ModVersionChecker/model/App.cs index 801c630..2148064 100644 --- a/ModVersionChecker/data/model/AppConfig.cs +++ b/ModVersionChecker/model/App.cs @@ -1,21 +1,21 @@ -using ModVersionChecker.data.model; -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; +using ModVersionChecker.enums; using System.Text.Json.Serialization; -using System.Threading.Tasks; -public class AppConfig +public class App { + public App() { } + [JsonPropertyName("id")] public string Id { get; set; } = string.Empty; + [JsonPropertyName("uid")] + public string Uid { get; set; } = string.Empty; + [JsonPropertyName("name")] public string Name { get; set; } = string.Empty; - [JsonPropertyName("msfsVersions")] - public List MsfsVersions { get; set; } = new List { "msfs2024" }; // Default to msfs2024 + [JsonPropertyName("type")] + public string Type { get; set; } = string.Empty; [JsonPropertyName("source")] @@ -24,8 +24,8 @@ public class AppConfig [JsonPropertyName("params")] public Dictionary Params { get; set; } = new Dictionary(); - [JsonPropertyName("fsFields")] - public Dictionary> FsFields { get; set; } = new Dictionary>(); + [JsonPropertyName("fields")] + public Dictionary Fields { get; set; } = new Dictionary(); [JsonPropertyName("downloadUrl")] public string DownloadUrl { get; set; } = string.Empty; @@ -37,15 +37,13 @@ public class AppConfig public string LatestVersion { get; set; } = string.Empty; [JsonPropertyName("status")] - public AppStatus Status { get; set; } = AppStatus.None; - - [JsonPropertyName("createdAt")] - public long CreatedAt { get; set; } = 0; - - [JsonPropertyName("updatedAt")] - public long UpdatedAt { get; set; } = 0; + [JsonConverter(typeof(JsonStringEnumConverter))] + public AppStatus Status { get; set; } = AppStatus.NONE; [JsonPropertyName("lastCheckedAt")] public long LastCheckedAt { get; set; } = 0; + [JsonPropertyName("localCheckedAt ")] + public long LocalCheckedAt { get; set; } = 0; + } diff --git a/ModVersionChecker/data/model/GlobalConfig.cs b/ModVersionChecker/model/Config.cs similarity index 52% rename from ModVersionChecker/data/model/GlobalConfig.cs rename to ModVersionChecker/model/Config.cs index 498bb0a..21de0dc 100644 --- a/ModVersionChecker/data/model/GlobalConfig.cs +++ b/ModVersionChecker/model/Config.cs @@ -6,11 +6,11 @@ using System.Text; using System.Text.Json.Serialization; using System.Threading.Tasks; -namespace ModVersionChecker.data.model +namespace ModVersionChecker.model { - public class GlobalConfig + public class Config { - public string Id { get; set; } = String.Empty; + public string Id { get; set; } = string.Empty; [JsonPropertyName("intervalMinutes")] public int IntervalMinutes { get; set; } = 60; @@ -20,5 +20,14 @@ namespace ModVersionChecker.data.model [JsonPropertyName("runOnStartup")] public bool RunOnStartup { get; set; } = false; + + [JsonPropertyName("types")] + public List Types { get; set; } = new List(); + + [JsonPropertyName("accessToekn")] + public string AccessToken { get; set; } = string.Empty; + + [JsonPropertyName("refreshToken")] + public string RefreshToken { get; set; } = string.Empty; } } diff --git a/ModVersionChecker/model/TypeConfig.cs b/ModVersionChecker/model/TypeConfig.cs new file mode 100644 index 0000000..59b1b7b --- /dev/null +++ b/ModVersionChecker/model/TypeConfig.cs @@ -0,0 +1,25 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Text.Json.Serialization; +using System.Threading.Tasks; + +namespace ModVersionChecker.model +{ + public class TypeConfig + + { + + public string Id { get; set; } = string.Empty; + + [JsonPropertyName("name")] + public string Name { get; set; } = string.Empty; + + [JsonPropertyName("shortName")] + public string ShortName { get; set; } = string.Empty; + + [JsonPropertyName("configValues")] + public Dictionary ConfigValues { get; set; } = new Dictionary(); + } +} diff --git a/ModVersionChecker/repository/api/ApiBase.cs b/ModVersionChecker/repository/api/ApiBase.cs new file mode 100644 index 0000000..6499154 --- /dev/null +++ b/ModVersionChecker/repository/api/ApiBase.cs @@ -0,0 +1,27 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace ModVersionChecker.repository.api +{ + public class ApiBase: IDisposable + { + private readonly HttpClient _httpClient = new HttpClient(); + + public ApiBase() + { + _httpClient.Timeout = TimeSpan.FromSeconds(30); + _httpClient.DefaultRequestHeaders.Add("User-Agent", "ModVersionChecker"); + } + + + + + public void Dispose() + { + _httpClient?.Dispose(); + } + } +} diff --git a/ModVersionChecker/repository/api/ApiRepository.cs b/ModVersionChecker/repository/api/ApiRepository.cs new file mode 100644 index 0000000..7a01223 --- /dev/null +++ b/ModVersionChecker/repository/api/ApiRepository.cs @@ -0,0 +1,169 @@ +using ModVersionChecker.repository.api.dto; +using System.Net.Http; +using System.Net.Http.Headers; +using System.Text; +using System.Text.Json; +using System.Collections.Generic; +using System.Threading.Tasks; + +namespace ModVersionChecker.repository.api +{ + public class ApiRepository : IApiRepository, IDisposable + { + private readonly HttpClient _httpClient; + private string baseUrl = "http://192.168.1.115:3115/api"; + private JwtTokenResponse? _accessToken; + private JwtTokenResponse? _refreshToken; + private DateTime _accessTokenExpiry = DateTime.MinValue; + + public ApiRepository() + { + _httpClient = new HttpClient(); + _httpClient.Timeout = TimeSpan.FromSeconds(30); + } + + public async Task AuthenticateAsync(string username, string password) + { + var url = $"{baseUrl}/auth"; + var payload = new { username, password }; + var jsonPayload = JsonSerializer.Serialize(payload); + var content = new StringContent(jsonPayload, Encoding.UTF8, "application/json"); + try + { + var response = await _httpClient.PostAsync(url, content); + response.EnsureSuccessStatusCode(); + var json = await response.Content.ReadAsStringAsync(); + var authentication = JsonSerializer.Deserialize(json, new JsonSerializerOptions { PropertyNameCaseInsensitive = true }); + _accessToken = DecodeJwt(authentication?.AccessToken); + _refreshToken = DecodeJwt(authentication?.RefreshToken); + if (_accessToken == null) + throw new Exception("Failed to decode access token."); + _accessTokenExpiry = DateTime.UtcNow.AddSeconds(_accessToken.ExpireAt - 60); + return true; + } + catch + { + return false; + } + } + + private JwtTokenResponse? DecodeJwt(string? token) + { + if (string.IsNullOrEmpty(token)) + return null; + var parts = token.Split('.'); + if (parts.Length != 3) + throw new ArgumentException("Invalid JWT token format."); + var payload = parts[1]; + var paddedPayload = payload.PadRight(payload.Length + (4 - payload.Length % 4) % 4, '='); + var jsonBytes = Convert.FromBase64String(paddedPayload); + var json = Encoding.UTF8.GetString(jsonBytes); + var doc = JsonSerializer.Deserialize(json, new JsonSerializerOptions { PropertyNameCaseInsensitive = true }); + if (doc != null) + doc.Token = token; + return doc; + } + + private async Task EnsureTokenValidAsync() + { + if (_accessToken == null || DateTime.UtcNow >= _accessTokenExpiry) + { + return await RefreshTokenAsync(); + } + return true; + } + + private async Task RefreshTokenAsync() + { + if (_refreshToken == null) + return false; + var refreshData = new { refreshToken = _refreshToken }; + var content = new StringContent(JsonSerializer.Serialize(refreshData), Encoding.UTF8, "application/json"); + var response = await _httpClient.PostAsync($"{baseUrl}/auth/refresh", content); + if (!response.IsSuccessStatusCode) + return false; + var json = await response.Content.ReadAsStringAsync(); + var authentication = JsonSerializer.Deserialize(json, new JsonSerializerOptions { PropertyNameCaseInsensitive = true }); + _accessToken = DecodeJwt(authentication?.AccessToken); + if (_accessToken == null) + throw new Exception("Failed to decode access token."); + _accessTokenExpiry = DateTime.UtcNow.AddSeconds(_accessToken.ExpireAt - 60); + return true; + } + + private async Task CreateRequestAsync(HttpMethod method, string url) + { + await EnsureTokenValidAsync(); + var request = new HttpRequestMessage(method, url); + if (_accessToken != null) + request.Headers.Authorization = new AuthenticationHeaderValue("Bearer", _accessToken.Token); + return request; + } + + public async Task?> GetAppVersionsAsync(List apps) + { + var url = $"{baseUrl}/app/versions?{string.Join("&", apps.Select(a => $"version={Uri.EscapeDataString(a.Id)}"))}"; + var request = await CreateRequestAsync(HttpMethod.Get, url); + var response = await _httpClient.SendAsync(request); + response.EnsureSuccessStatusCode(); + var json = await response.Content.ReadAsStringAsync(); + return JsonSerializer.Deserialize>(json, new JsonSerializerOptions { PropertyNameCaseInsensitive = true }); + } + public async Task GetAppLatestVersionAsync(App app) + { + var url = $"{baseUrl}/app/latest?version={Uri.EscapeDataString(app.Id)}"; + var request = await CreateRequestAsync(HttpMethod.Get, url); + var response = await _httpClient.SendAsync(request); + response.EnsureSuccessStatusCode(); + var json = await response.Content.ReadAsStringAsync(); + return JsonSerializer.Deserialize(json, new JsonSerializerOptions { PropertyNameCaseInsensitive = true }); + } + + public async Task> GetTypes() + { + var url = $"{baseUrl}/type"; + var request = await CreateRequestAsync(HttpMethod.Get, url); + var response = await _httpClient.SendAsync(request); + response.EnsureSuccessStatusCode(); + var json = await response.Content.ReadAsStringAsync(); + return JsonSerializer.Deserialize>(json, new JsonSerializerOptions { PropertyNameCaseInsensitive = true }); + } + + public async Task> GetSources() + { + var url = $"{baseUrl}/source"; + var request = await CreateRequestAsync(HttpMethod.Get, url); + var response = await _httpClient.SendAsync(request); + response.EnsureSuccessStatusCode(); + var json = await response.Content.ReadAsStringAsync(); + return JsonSerializer.Deserialize>(json, new JsonSerializerOptions { PropertyNameCaseInsensitive = true }); + } + + public async Task?> SearchApps(string searchText) + { + var url = $"{baseUrl}/app/search?query={Uri.EscapeDataString(searchText)}"; + var request = await CreateRequestAsync(HttpMethod.Get, url); + var response = await _httpClient.SendAsync(request); + response.EnsureSuccessStatusCode(); + var json = await response.Content.ReadAsStringAsync(); + return JsonSerializer.Deserialize>(json, new JsonSerializerOptions { PropertyNameCaseInsensitive = true }); + } + + public async Task> GetAppsByIds(App[] apps) + { + var query = string.Join("&", apps.Select(a => $"id={Uri.EscapeDataString(a.Id)}")); + var url = $"{baseUrl}/app/search?{query}"; + var request = await CreateRequestAsync(HttpMethod.Get, url); + var response = await _httpClient.SendAsync(request); + response.EnsureSuccessStatusCode(); + var json = await response.Content.ReadAsStringAsync(); + return JsonSerializer.Deserialize>(json, new JsonSerializerOptions { PropertyNameCaseInsensitive = true }) ?? new List(); + } + + + public void Dispose() + { + _httpClient?.Dispose(); + } + } +} \ No newline at end of file diff --git a/ModVersionChecker/repository/api/IApiRepository.cs b/ModVersionChecker/repository/api/IApiRepository.cs new file mode 100644 index 0000000..a1fb918 --- /dev/null +++ b/ModVersionChecker/repository/api/IApiRepository.cs @@ -0,0 +1,17 @@ +using System.Threading.Tasks; +using System.Collections.Generic; +using ModVersionChecker.repository.api.dto; + +namespace ModVersionChecker.repository.api +{ + public interface IApiRepository + { + Task AuthenticateAsync(string username, string password); + Task?> GetAppVersionsAsync(List apps); + Task GetAppLatestVersionAsync(App app); + Task?> SearchApps(string searchText); + Task> GetTypes(); + Task> GetSources(); + Task> GetAppsByIds(App[] apps); + } +} \ No newline at end of file diff --git a/ModVersionChecker/repository/api/dto/AppResponse.cs b/ModVersionChecker/repository/api/dto/AppResponse.cs new file mode 100644 index 0000000..d5becfb --- /dev/null +++ b/ModVersionChecker/repository/api/dto/AppResponse.cs @@ -0,0 +1,81 @@ +using ModVersionChecker.enums; +using System.Text.Json.Serialization; + +namespace ModVersionChecker.repository.api.dto +{ + public class AppResponse + { + public AppResponse() { } + + [JsonPropertyName("id")] + public string Id { get; set; } = string.Empty; + + [JsonPropertyName("uid")] + public string Uid { get; set; } = string.Empty; + + [JsonPropertyName("name")] + public string Name { get; set; } = string.Empty; + + [JsonPropertyName("type")] + public string Type { get; set; } = string.Empty; + + + [JsonPropertyName("source")] + public string Source { get; set; } = string.Empty; + + [JsonPropertyName("params")] + public Dictionary Params { get; set; } = new Dictionary(); + + [JsonPropertyName("fields")] + public Dictionary Fields { get; set; } = new Dictionary(); + + [JsonPropertyName("downloadUrl")] + public string DownloadUrl { get; set; } = string.Empty; + + [JsonPropertyName("currentVersion")] + public string CurrentVersion { get; set; } = string.Empty; + + [JsonPropertyName("latestVersion")] + public string LatestVersion { get; set; } = string.Empty; + + [JsonPropertyName("status")] + [JsonConverter(typeof(JsonStringEnumConverter))] + public AppStatus Status { get; set; } = AppStatus.NONE; + + [JsonPropertyName("createdAt")] + public long CreatedAt { get; set; } = 0; + + [JsonPropertyName("updatedAt")] + public long UpdatedAt { get; set; } = 0; + + [JsonPropertyName("lastCheckedAt")] + public long LastCheckedAt { get; set; } = 0; + + [JsonPropertyName("active")] + public bool Active { get; set; } = false; + + public static App toModel(AppResponse appResponse) + { + if (appResponse == null) + { + return new App(); + } + return new App() + { + Id = appResponse.Id, + Uid = appResponse.Uid, + Name = appResponse.Name, + Type = appResponse.Type, + Source = appResponse.Source, + Params = appResponse.Params, + Fields = appResponse.Fields, + DownloadUrl = appResponse.DownloadUrl, + CurrentVersion = appResponse.CurrentVersion, + LatestVersion = appResponse.LatestVersion, + Status = appResponse.Status, + LastCheckedAt = appResponse.LastCheckedAt, + + }; + } + } +} diff --git a/ModVersionChecker/repository/api/dto/AppVersionsResponse.cs b/ModVersionChecker/repository/api/dto/AppVersionsResponse.cs new file mode 100644 index 0000000..27cb0c0 --- /dev/null +++ b/ModVersionChecker/repository/api/dto/AppVersionsResponse.cs @@ -0,0 +1,15 @@ +using System.Text.Json.Serialization; + +namespace ModVersionChecker.repository.api.dto +{ + public class AppVersionsResponse + { + public AppVersionsResponse() { } + + [JsonPropertyName("id")] + public string Id { get; set; } = string.Empty; + + [JsonPropertyName("latestVersion")] + public string LatestVersion { get; set; } = string.Empty; + } +} \ No newline at end of file diff --git a/ModVersionChecker/repository/api/dto/AuthenticationResponse.cs b/ModVersionChecker/repository/api/dto/AuthenticationResponse.cs new file mode 100644 index 0000000..19f8da6 --- /dev/null +++ b/ModVersionChecker/repository/api/dto/AuthenticationResponse.cs @@ -0,0 +1,18 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Text.Json.Serialization; +using System.Threading.Tasks; + +namespace ModVersionChecker.repository.api.dto +{ + public class AuthenticationResponse + { + [JsonPropertyName("accessToken")] + public string AccessToken { get; set; } = string.Empty; + + [JsonPropertyName("refreshToken")] + public string RefreshToken { get; set; } = string.Empty; + } +} diff --git a/ModVersionChecker/data/model/FieldDef.cs b/ModVersionChecker/repository/api/dto/FieldResponse.cs similarity index 57% rename from ModVersionChecker/data/model/FieldDef.cs rename to ModVersionChecker/repository/api/dto/FieldResponse.cs index c91cbff..f8c6179 100644 --- a/ModVersionChecker/data/model/FieldDef.cs +++ b/ModVersionChecker/repository/api/dto/FieldResponse.cs @@ -5,15 +5,17 @@ using System.Text; using System.Text.Json.Serialization; using System.Threading.Tasks; -namespace ModVersionChecker.data.model +namespace ModVersionChecker.repository.api.dto { - public class FieldDef + public class FieldResponse { [JsonPropertyName("name")] public string Name { get; set; } = string.Empty; [JsonPropertyName("label")] public string Label { get; set; } = string.Empty; + [JsonPropertyName("description")] + public string Description { get; set; } = string.Empty; [JsonPropertyName("type")] public string Type { get; set; } = string.Empty; @@ -21,7 +23,10 @@ namespace ModVersionChecker.data.model [JsonPropertyName("required")] public bool Required { get; set; } = false; - [JsonPropertyName("control")] - public string Control { get; set; } = string.Empty; + [JsonPropertyName("controlType")] + public string ControlType { get; set; } = string.Empty; + + [JsonPropertyName("defaultValue")] + public string DefaultValue { get; set; } = string.Empty; } } diff --git a/ModVersionChecker/repository/api/dto/JwtTokenResponse.cs b/ModVersionChecker/repository/api/dto/JwtTokenResponse.cs new file mode 100644 index 0000000..adf0464 --- /dev/null +++ b/ModVersionChecker/repository/api/dto/JwtTokenResponse.cs @@ -0,0 +1,26 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Text.Json.Serialization; +using System.Threading.Tasks; + +namespace ModVersionChecker.repository.api.dto +{ + public class JwtTokenResponse + { + public JwtTokenResponse() { } + public string Token { get; set; } = string.Empty; + + [JsonPropertyName("exp")] + public long ExpireAt { get; set; } = 0; + + [JsonPropertyName("iat")] + public long IssuedAt { get; set; } = 0; + + [JsonPropertyName("sub")] + public string Subject { get; set; } = string.Empty; + + + } +} diff --git a/ModVersionChecker/data/model/SourceDef.cs b/ModVersionChecker/repository/api/dto/SourceResponse.cs similarity index 65% rename from ModVersionChecker/data/model/SourceDef.cs rename to ModVersionChecker/repository/api/dto/SourceResponse.cs index aad8453..844105e 100644 --- a/ModVersionChecker/data/model/SourceDef.cs +++ b/ModVersionChecker/repository/api/dto/SourceResponse.cs @@ -1,13 +1,8 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Text.Json.Serialization; -using System.Threading.Tasks; +using System.Text.Json.Serialization; -namespace ModVersionChecker.data.model +namespace ModVersionChecker.repository.api.dto { - public class SourceDef + public class SourceResponse { [JsonPropertyName("id")] public string Id { get; set; } = string.Empty; diff --git a/ModVersionChecker/repository/api/dto/TypeResponse.cs b/ModVersionChecker/repository/api/dto/TypeResponse.cs new file mode 100644 index 0000000..15a02a5 --- /dev/null +++ b/ModVersionChecker/repository/api/dto/TypeResponse.cs @@ -0,0 +1,27 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Text.Json.Serialization; +using System.Threading.Tasks; + +namespace ModVersionChecker.repository.api.dto +{ + public class TypeResponse + { + + public string Id { get; set; } = String.Empty; + + [JsonPropertyName("name")] + public string Name { get; set; } = string.Empty; + + [JsonPropertyName("shortName")] + public string ShortName { get; set; } = string.Empty; + + [JsonPropertyName("configFields")] + public List ConfigFields { get; set; } = new List(); + + [JsonPropertyName("appFields")] + public List AppFields { get; set; } = new List(); + } +} diff --git a/ModVersionChecker/managers/filesystem/AppStatusManager.cs b/ModVersionChecker/repository/filesystem/AppStatusManager.cs similarity index 97% rename from ModVersionChecker/managers/filesystem/AppStatusManager.cs rename to ModVersionChecker/repository/filesystem/AppStatusManager.cs index 69b948a..9159099 100644 --- a/ModVersionChecker/managers/filesystem/AppStatusManager.cs +++ b/ModVersionChecker/repository/filesystem/AppStatusManager.cs @@ -1,4 +1,4 @@ -using ModVersionChecker.data.model; +using ModVersionChecker.enums; using ModVersionChecker.managers.interfaces; using System; using System.Collections.Generic; diff --git a/ModVersionChecker/managers/filesystem/AppsManager.cs b/ModVersionChecker/repository/filesystem/AppsManager.cs similarity index 78% rename from ModVersionChecker/managers/filesystem/AppsManager.cs rename to ModVersionChecker/repository/filesystem/AppsManager.cs index 8c75406..e154aed 100644 --- a/ModVersionChecker/managers/filesystem/AppsManager.cs +++ b/ModVersionChecker/repository/filesystem/AppsManager.cs @@ -9,22 +9,22 @@ namespace ModVersionChecker.managers.filesystem { private readonly string FilePath = Path.Combine(AppContext.BaseDirectory, "data", "apps.json"); - public List Load() + public List Load() { if (!File.Exists(FilePath)) - return new List(); + return new List(); var json = File.ReadAllText(FilePath); - return JsonSerializer.Deserialize>(json) ?? new(); + return JsonSerializer.Deserialize>(json) ?? new(); } - public void Save(List apps) + public void Save(List apps) { var options = new JsonSerializerOptions { WriteIndented = true }; var json = JsonSerializer.Serialize(apps, options); File.WriteAllText(FilePath, json); } - public void Upsert(AppConfig app) + public void Upsert(App app) { var apps = Load(); var index = apps.FindIndex(a => a.Id == app.Id); diff --git a/ModVersionChecker/managers/filesystem/ConfigManager.cs b/ModVersionChecker/repository/filesystem/ConfigManager.cs similarity index 66% rename from ModVersionChecker/managers/filesystem/ConfigManager.cs rename to ModVersionChecker/repository/filesystem/ConfigManager.cs index b4bba36..36a927e 100644 --- a/ModVersionChecker/managers/filesystem/ConfigManager.cs +++ b/ModVersionChecker/repository/filesystem/ConfigManager.cs @@ -1,5 +1,5 @@ -using ModVersionChecker.data.model; -using ModVersionChecker.managers.interfaces; +using ModVersionChecker.managers.interfaces; +using ModVersionChecker.model; using System; using System.IO; using System.Text.Json; @@ -10,27 +10,27 @@ namespace ModVersionChecker.managers.filesystem { private static readonly string _filePath = Path.Combine(AppContext.BaseDirectory, "data", "config.json"); - private GlobalConfig _config; + private Config _config; public ConfigManager() { _config = Load(); } - public GlobalConfig Load() + public Config Load() { if (!File.Exists(_filePath)) - return new GlobalConfig(); + return new Config(); var json = File.ReadAllText(_filePath); - return JsonSerializer.Deserialize(json) ?? new GlobalConfig(); + return JsonSerializer.Deserialize(json) ?? new Config(); } - public GlobalConfig GetConfig() + public Config GetConfig() { return _config; } - public void Save(GlobalConfig config) + public void Save(Config config) { var options = new JsonSerializerOptions { WriteIndented = true }; var json = JsonSerializer.Serialize(config, options); diff --git a/ModVersionChecker/managers/filesystem/SourcesDefManager.cs b/ModVersionChecker/repository/filesystem/SourcesDefManager.cs similarity index 66% rename from ModVersionChecker/managers/filesystem/SourcesDefManager.cs rename to ModVersionChecker/repository/filesystem/SourcesDefManager.cs index 52dfd73..f556f1c 100644 --- a/ModVersionChecker/managers/filesystem/SourcesDefManager.cs +++ b/ModVersionChecker/repository/filesystem/SourcesDefManager.cs @@ -1,5 +1,5 @@ -using ModVersionChecker.data.model; -using ModVersionChecker.managers.interfaces; +using ModVersionChecker.managers.interfaces; +using ModVersionChecker.repository.api.dto; using System.Collections.Generic; using System.IO; using System.Text.Json; @@ -9,27 +9,27 @@ namespace ModVersionChecker.managers.filesystem public class SourcesDefManager { private readonly string _filePath = Path.Combine(AppContext.BaseDirectory, "data", "sourcesDef.json"); - private List _sourcesDef = new List(); + private List _sourcesDef = new List(); public SourcesDefManager() { _sourcesDef = Load(); } - private List Load() + private List Load() { if (!File.Exists(_filePath)) - return new List(); + return new List(); var json = File.ReadAllText(_filePath); - return JsonSerializer.Deserialize>(json) ?? new(); + return JsonSerializer.Deserialize>(json) ?? new(); } - public List GetSourcesDef() + public List GetSourcesDef() { return _sourcesDef; } - public void AddSourceDef(SourceDef sourceDef) + public void AddSourceDef(SourceResponse sourceDef) { _sourcesDef.Add(sourceDef); Save(_sourcesDef); @@ -42,7 +42,7 @@ namespace ModVersionChecker.managers.filesystem } - public void Save(List sourcesDef) + public void Save(List sourcesDef) { var options = new JsonSerializerOptions { WriteIndented = true }; var json = JsonSerializer.Serialize(sourcesDef, options); diff --git a/ModVersionChecker/managers/interfaces/IAppStatusManager.cs b/ModVersionChecker/repository/interfaces/IAppStatusManager.cs similarity index 92% rename from ModVersionChecker/managers/interfaces/IAppStatusManager.cs rename to ModVersionChecker/repository/interfaces/IAppStatusManager.cs index b103070..b4e0973 100644 --- a/ModVersionChecker/managers/interfaces/IAppStatusManager.cs +++ b/ModVersionChecker/repository/interfaces/IAppStatusManager.cs @@ -1,4 +1,4 @@ -using ModVersionChecker.data.model; +using ModVersionChecker.enums; using System; using System.Collections.Generic; using System.Linq; diff --git a/ModVersionChecker/managers/interfaces/IAppsManager.cs b/ModVersionChecker/repository/interfaces/IAppsManager.cs similarity index 51% rename from ModVersionChecker/managers/interfaces/IAppsManager.cs rename to ModVersionChecker/repository/interfaces/IAppsManager.cs index f101df3..955d0e5 100644 --- a/ModVersionChecker/managers/interfaces/IAppsManager.cs +++ b/ModVersionChecker/repository/interfaces/IAppsManager.cs @@ -1,4 +1,4 @@ -using ModVersionChecker.data.model; +using ModVersionChecker.enums; using System; using System.Collections.Generic; using System.Linq; @@ -10,17 +10,17 @@ namespace ModVersionChecker.managers.interfaces public interface IAppsManager { - List Load(); + List Load(); - void Save(List apps); + void Save(List apps); - public void Insert(AppConfig app); + public void Insert(App app); - public void Update(AppConfig app); + public void Update(App app); void Delete(string id); - void UpdateStatus(AppConfig app, AppStatus status); + void UpdateStatus(App app, AppStatus status); } } diff --git a/ModVersionChecker/managers/interfaces/IConfigManager.cs b/ModVersionChecker/repository/interfaces/IConfigManager.cs similarity index 60% rename from ModVersionChecker/managers/interfaces/IConfigManager.cs rename to ModVersionChecker/repository/interfaces/IConfigManager.cs index 6efd1e0..161e0c6 100644 --- a/ModVersionChecker/managers/interfaces/IConfigManager.cs +++ b/ModVersionChecker/repository/interfaces/IConfigManager.cs @@ -1,4 +1,4 @@ -using ModVersionChecker.data.model; +using ModVersionChecker.model; using System; using System.Collections.Generic; using System.Linq; @@ -9,8 +9,8 @@ namespace ModVersionChecker.managers.interfaces { public interface IConfigManager { - GlobalConfig Load(); - void Save(GlobalConfig config); - GlobalConfig GetConfig(); + Config Load(); + void Save(Config config); + Config GetConfig(); } } diff --git a/ModVersionChecker/managers/interfaces/IFlightSimsManager.cs b/ModVersionChecker/repository/interfaces/IFlightSimsManager.cs similarity index 52% rename from ModVersionChecker/managers/interfaces/IFlightSimsManager.cs rename to ModVersionChecker/repository/interfaces/IFlightSimsManager.cs index 0681204..c2686c3 100644 --- a/ModVersionChecker/managers/interfaces/IFlightSimsManager.cs +++ b/ModVersionChecker/repository/interfaces/IFlightSimsManager.cs @@ -1,4 +1,4 @@ -using ModVersionChecker.data.model; +using ModVersionChecker.repository.api.dto; using System; using System.Collections.Generic; using System.Linq; @@ -9,9 +9,11 @@ namespace ModVersionChecker.managers.interfaces { public interface IFlightSimsManager { - List Load(); - void Save(FsModPathConfig config); + List Load(); + void Save(TypeResponse config); - FsModPathConfig? GetByShortName(string id); + TypeResponse? GetByShortName(string id); + + void DeleteAll(); } } diff --git a/ModVersionChecker/managers/interfaces/ISourcesDefManager.cs b/ModVersionChecker/repository/interfaces/ISourcesDefManager.cs similarity index 51% rename from ModVersionChecker/managers/interfaces/ISourcesDefManager.cs rename to ModVersionChecker/repository/interfaces/ISourcesDefManager.cs index 9b327b7..3b3840f 100644 --- a/ModVersionChecker/managers/interfaces/ISourcesDefManager.cs +++ b/ModVersionChecker/repository/interfaces/ISourcesDefManager.cs @@ -1,4 +1,4 @@ -using ModVersionChecker.data.model; +using ModVersionChecker.repository.api.dto; using System; using System.Collections.Generic; using System.Linq; @@ -9,11 +9,12 @@ namespace ModVersionChecker.managers.interfaces { public interface ISourcesDefManager { - List List(); + List List(); - SourceDef? GetById(string id); - void AddSourceDef(SourceDef sourceDef); + SourceResponse? GetById(string id); + void AddSourceDef(SourceResponse sourceDef); void RemoveSourceDef(string id); - void Save(SourceDef sourceDef); + void Save(SourceResponse sourceDef); + void DeleteAll(); } } diff --git a/ModVersionChecker/repository/interfaces/ITypeManager.cs b/ModVersionChecker/repository/interfaces/ITypeManager.cs new file mode 100644 index 0000000..278a030 --- /dev/null +++ b/ModVersionChecker/repository/interfaces/ITypeManager.cs @@ -0,0 +1,13 @@ +using ModVersionChecker.model; + +namespace ModVersionChecker.managers.interfaces +{ + public interface ITypeManager + { + List GetTypeConfigs(); + void SaveTypeConfigs(List types); + TypeConfig? GetTypeConfigById(string id); + void SaveTypeConfig(TypeConfig type); + void DeleteTypeConfig(string id); + } +} diff --git a/ModVersionChecker/repository/litedb/AppLiteDb.cs b/ModVersionChecker/repository/litedb/AppLiteDb.cs new file mode 100644 index 0000000..d94e8ab --- /dev/null +++ b/ModVersionChecker/repository/litedb/AppLiteDb.cs @@ -0,0 +1,50 @@ +using ModVersionChecker.enums; +using ModVersionChecker.managers.interfaces; +using ModVersionChecker.utils; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace ModVersionChecker.managers.litedb +{ + public class AppLiteDb : LiteDb, IAppsManager + { + protected override string collection => LiteDb.APPS_COLLECTION; + + public List Load() + { + return GetCollection().FindAll().ToList(); + } + + public void Insert(App app) + { + var now = TimeUtils.GetUnixTimeMillis(null); + GetCollection().Insert(app); + } + + public void Update(App app) + { + var now = TimeUtils.GetUnixTimeMillis(null); + GetCollection().Update(app); + } + + public void Delete(string id) + { + GetCollection().Delete(id); + } + + public void Save(List apps) + { + + } + + public void UpdateStatus(App app, AppStatus status) + { + app.LastCheckedAt = TimeUtils.GetUnixTimeMillis(null); + app.Status = status; + GetCollection().Update(app); + } + } +} diff --git a/ModVersionChecker/repository/litedb/ConfigLiteDb.cs b/ModVersionChecker/repository/litedb/ConfigLiteDb.cs new file mode 100644 index 0000000..4c7510c --- /dev/null +++ b/ModVersionChecker/repository/litedb/ConfigLiteDb.cs @@ -0,0 +1,22 @@ +using ModVersionChecker.managers.interfaces; +using ModVersionChecker.model; + +namespace ModVersionChecker.managers.litedb +{ + public class ConfigLiteDb : LiteDb, IConfigManager + { + protected override string collection => LiteDb.CONFIG_COLLECTION; + public Config Load() + { + return GetCollection().FindAll().FirstOrDefault() ?? new Config(); + } + public void Save(Config config) + { + GetCollection().Upsert(config); + } + public Config GetConfig() + { + return Load(); + } + } +} diff --git a/ModVersionChecker/managers/litedb/LiteDb.cs b/ModVersionChecker/repository/litedb/LiteDb.cs similarity index 61% rename from ModVersionChecker/managers/litedb/LiteDb.cs rename to ModVersionChecker/repository/litedb/LiteDb.cs index e3f35d5..355da6b 100644 --- a/ModVersionChecker/managers/litedb/LiteDb.cs +++ b/ModVersionChecker/repository/litedb/LiteDb.cs @@ -2,7 +2,7 @@ namespace ModVersionChecker.managers.litedb { - public class LiteDb + public abstract class LiteDb { public static string DB_PATH = "ModVersionChecker.db"; public static string APPS_COLLECTION = "apps"; @@ -10,7 +10,16 @@ namespace ModVersionChecker.managers.litedb public static string SOURCES_DEF_COLLECTION = "sources_def"; public static string CONFIG_COLLECTION = "config"; public static string FLIGHT_SIMS_COLLECTION = "flight_sims"; + public static string TYPES_COLLECTION = "types"; + public static string LOCAL_APPS_COLLECTION = "local_apps"; protected LiteDatabase _db = LiteDbSingleton.Instance; + + protected abstract string collection { get; } + + protected ILiteCollection GetCollection() + { + return _db.GetCollection(collection); + } } } diff --git a/ModVersionChecker/repository/litedb/SourcesLiteDb.cs b/ModVersionChecker/repository/litedb/SourcesLiteDb.cs new file mode 100644 index 0000000..7a58105 --- /dev/null +++ b/ModVersionChecker/repository/litedb/SourcesLiteDb.cs @@ -0,0 +1,44 @@ +using ModVersionChecker.managers.interfaces; +using ModVersionChecker.repository.api.dto; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace ModVersionChecker.managers.litedb +{ + public class SourcesLiteDb : LiteDb, ISourcesDefManager + { + protected override string collection => SOURCES_DEF_COLLECTION; + + public List List() + { + return GetCollection().FindAll().ToList(); + } + + public SourceResponse? GetById(string id) + { + return GetCollection().FindOne(x => x.Id == id); + } + + public void AddSourceDef(SourceResponse sourceDef) + { + GetCollection().Insert(sourceDef); + } + public void RemoveSourceDef(string id) + { + GetCollection().Delete(id); + } + + public void Save(SourceResponse sourceDef) + { + GetCollection().Upsert(sourceDef); + } + + public void DeleteAll() + { + GetCollection().DeleteAll(); + } + } +} diff --git a/ModVersionChecker/repository/litedb/TypeConfigLiteDb.cs b/ModVersionChecker/repository/litedb/TypeConfigLiteDb.cs new file mode 100644 index 0000000..14aee6e --- /dev/null +++ b/ModVersionChecker/repository/litedb/TypeConfigLiteDb.cs @@ -0,0 +1,35 @@ +using ModVersionChecker.managers.interfaces; +using ModVersionChecker.model; + +namespace ModVersionChecker.managers.litedb +{ + internal class TypeConfigLiteDb : LiteDb, ITypeManager + { + protected override string collection => TYPES_COLLECTION; + + public void DeleteTypeConfig(string id) + { + GetCollection().Delete(id); + } + + public TypeConfig? GetTypeConfigById(string id) + { + return GetCollection().FindOne(x => x.Id == id); + } + + public List GetTypeConfigs() + { + return GetCollection().FindAll().ToList(); + } + + public void SaveTypeConfig(TypeConfig type) + { + GetCollection().Upsert(type); + } + + public void SaveTypeConfigs(List types) + { + GetCollection().InsertBulk(types); + } + } +} diff --git a/ModVersionChecker/repository/litedb/TypeLiteDb.cs b/ModVersionChecker/repository/litedb/TypeLiteDb.cs new file mode 100644 index 0000000..5ea1246 --- /dev/null +++ b/ModVersionChecker/repository/litedb/TypeLiteDb.cs @@ -0,0 +1,35 @@ +using ModVersionChecker.managers.interfaces; +using ModVersionChecker.repository.api.dto; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace ModVersionChecker.managers.litedb +{ + internal class TypeLiteDb : LiteDb, IFlightSimsManager + { + protected override string collection => FLIGHT_SIMS_COLLECTION; + + public List Load() + { + return GetCollection().FindAll().ToList(); + } + + public void Save(TypeResponse config) + { + GetCollection().Upsert(config); + } + + public TypeResponse? GetByShortName(string id) + { + return GetCollection().FindOne(x => x.ShortName == id); + } + + public void DeleteAll() + { + GetCollection().DeleteAll(); + } + } +} diff --git a/ModVersionChecker/service/ApiService.cs b/ModVersionChecker/service/ApiService.cs new file mode 100644 index 0000000..39a59ed --- /dev/null +++ b/ModVersionChecker/service/ApiService.cs @@ -0,0 +1,47 @@ +using ModVersionChecker.repository.api; +using ModVersionChecker.repository.api.dto; +using ModVersionChecker.service.interfaces; +using System.Collections.Generic; +using System.Threading.Tasks; + +namespace ModVersionChecker.service +{ + public class ApiService : IApiService + { + private readonly IApiRepository _apiRepository; + + public ApiService(IApiRepository apiRepository) + { + _apiRepository = apiRepository; + } + + public Task AuthenticateAsync(string username, string password) + => _apiRepository.AuthenticateAsync(username, password); + + public Task?> GetAppVersionsAsync(List apps) + => _apiRepository.GetAppVersionsAsync(apps); + + public Task GetAppLatestVersionAsync(App app) + => _apiRepository.GetAppLatestVersionAsync(app); + + + public Task> GetTypes() + => _apiRepository.GetTypes(); + + public Task> GetSources() + => _apiRepository.GetSources(); + + public async Task> GetAppsByIds(App[] apps) + { + var appResponses = await _apiRepository.GetAppsByIds(apps); + return appResponses.Select(AppResponse.toModel).ToList(); + } + + public Task?> SearchApps(string searchText) + { + var appResponses = _apiRepository.SearchApps(searchText); + return appResponses.ContinueWith(t => t.Result?.Select(AppResponse.toModel).ToList()); + } + + } +} \ No newline at end of file diff --git a/ModVersionChecker/managers/filesystem/NotifyIconService.cs b/ModVersionChecker/service/NotifyIconService.cs similarity index 84% rename from ModVersionChecker/managers/filesystem/NotifyIconService.cs rename to ModVersionChecker/service/NotifyIconService.cs index fbb70d2..fdb06b4 100644 --- a/ModVersionChecker/managers/filesystem/NotifyIconService.cs +++ b/ModVersionChecker/service/NotifyIconService.cs @@ -1,11 +1,11 @@ -using ModVersionChecker.managers.interfaces; +using ModVersionChecker.service.interfaces; using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; -namespace ModVersionChecker.managers.filesystem +namespace ModVersionChecker.service { public class NotifyIconService : INotifyIconService { diff --git a/ModVersionChecker/service/VersionService.cs b/ModVersionChecker/service/VersionService.cs new file mode 100644 index 0000000..4347525 --- /dev/null +++ b/ModVersionChecker/service/VersionService.cs @@ -0,0 +1,141 @@ +using ModVersionChecker.enums; +using ModVersionChecker.managers.interfaces; +using ModVersionChecker.model; +using ModVersionChecker.service.interfaces; +using ModVersionChecker.utils; +using NuGet.Versioning; + +namespace ModVersionChecker.service +{ + public class VersionService: IVersionService + { + private readonly IApiService _apiService; + private readonly IAppsManager _appsManager; + private readonly INotifyIconService _notifyIconService; + private readonly Config _globalConfig; + + public VersionService( + IApiService apiVersionService, + IAppsManager appsManager, + IConfigManager configManager, + INotifyIconService notifyIconService) + { + _apiService = apiVersionService; + _appsManager = appsManager ?? throw new ArgumentNullException(nameof(appsManager)); + _notifyIconService = notifyIconService ?? throw new ArgumentNullException(nameof(notifyIconService)); + _globalConfig = configManager.Load() ?? new Config(); + } + + public App CheckApp(App app) + { + var status = AppStatus.NONE; + if (app.LatestVersion== null) + { + app.Status = AppStatus.ERROR; + _appsManager.Update(app); + return app; + } + var currentVersion = app.CurrentVersion; + var updateMessage = ""; + if (isUpdateAvailable(currentVersion, app.LatestVersion)) + { + updateMessage = $"{app.Name}: New version {app.LatestVersion} (current: {currentVersion})"; + status = AppStatus.UPDATE_AVAILABLE; + } + app.Status = status; + app.LastCheckedAt = TimeUtils.GetUnixTimeMillis(null); + _appsManager.Update(app); + return app; + } + + public async Task CheckAllApps() + { + + var apps = _appsManager.Load(); + var sources = await _apiService.GetSources(); + var appVersionsMap = await _apiService.GetAppVersionsAsync(apps); + List errorMessages = new List(); + List updateMessages = new List(); + + if (apps == null || apps.Count == 0) + { + return; + } + foreach (App app in apps) + { + + var sourceId = app.Source; + + if ( + app.Status != AppStatus.ERROR && app.LastCheckedAt != 0 && app.LastCheckedAt < TimeUtils.GetUnixTimeMillis(DateTime.UtcNow.AddMinutes(-60)) || + !app.Active || string.IsNullOrWhiteSpace(sourceId) + ) + { + continue; + } + + try + { + var latesstVersion = appVersionsMap.FirstOrDefault(a => a.Id == app.Id)?.LatestVersion; + var source = sources.FirstOrDefault(s => s.Id == sourceId); + if (source == null) + { + errorMessages.Add($"{app.Name} has an invalid source: {sourceId}"); + continue; + } + var typeConfig = _globalConfig.Types.FirstOrDefault(t => t.ShortName == app.Type); + if (typeConfig == null) + { + errorMessages.Add($"{app.Name} has no valid type config."); + continue; + } + app.CurrentVersion = VersionUtils.GetCurrentVersion(app, typeConfig); + + var updatedApp = CheckApp(app); + if (updatedApp.Status == AppStatus.UPDATE_AVAILABLE) + { + updateMessages.Add($"{app.Name}: New version {app.LatestVersion} (current: {app.CurrentVersion})"); + } + } + catch (Exception ex) + { + errorMessages.Add($"Failed to check {app.Name}: {ex.Message}"); + } + } + + if (updateMessages.Count > 0) + { + _notifyIconService.ShowBalloonTip( + 10000, + "Updates Available", + string.Join("\n", updateMessages), + ToolTipIcon.Info + ); + } + if (errorMessages.Count > 0) + { + _notifyIconService.ShowBalloonTip( + 10000, + "Errors", + string.Join("\n", errorMessages), + ToolTipIcon.Error + ); + } + } + + private bool isUpdateAvailable(string currentVersion, string latestVersion) + { + try + { + var current = NuGetVersion.Parse(currentVersion); + var latest = NuGetVersion.Parse(latestVersion); + return latest.CompareTo(current) == 1; + } + catch (Exception ex) + { + Console.WriteLine($"Failed to compare versions: {ex.Message}"); + return false; + } + } + } +} diff --git a/ModVersionChecker/service/interfaces/IApiService.cs b/ModVersionChecker/service/interfaces/IApiService.cs new file mode 100644 index 0000000..9f8c8da --- /dev/null +++ b/ModVersionChecker/service/interfaces/IApiService.cs @@ -0,0 +1,19 @@ +using ModVersionChecker.repository.api.dto; +using NuGet.Versioning; +using System.Collections.Generic; +using System.Threading.Tasks; + +namespace ModVersionChecker.service.interfaces +{ + public interface IApiService + { + Task> GetAppVersionsAsync(List apps); + Task> GetTypes(); + Task> GetSources(); + Task GetAppLatestVersionAsync(App app); + Task AuthenticateAsync(string username, string password); + Task> GetAppsByIds(App[] apps); + Task?> SearchApps(string searchText); + + } +} \ No newline at end of file diff --git a/ModVersionChecker/managers/interfaces/INotifyIconService.cs b/ModVersionChecker/service/interfaces/INotifyIconService.cs similarity index 86% rename from ModVersionChecker/managers/interfaces/INotifyIconService.cs rename to ModVersionChecker/service/interfaces/INotifyIconService.cs index a8e35c1..062a0f0 100644 --- a/ModVersionChecker/managers/interfaces/INotifyIconService.cs +++ b/ModVersionChecker/service/interfaces/INotifyIconService.cs @@ -4,7 +4,7 @@ using System.Linq; using System.Text; using System.Threading.Tasks; -namespace ModVersionChecker.managers.interfaces +namespace ModVersionChecker.service.interfaces { public interface INotifyIconService { diff --git a/ModVersionChecker/service/interfaces/IVersionService.cs b/ModVersionChecker/service/interfaces/IVersionService.cs new file mode 100644 index 0000000..734cdb2 --- /dev/null +++ b/ModVersionChecker/service/interfaces/IVersionService.cs @@ -0,0 +1,15 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace ModVersionChecker.service.interfaces +{ + public interface IVersionService + { + App CheckApp(App app); + + Task CheckAllApps(); + } +} diff --git a/ModVersionChecker/ui/controls/DirectoryPickerControl.cs b/ModVersionChecker/ui/controls/DirectoryPickerControl.cs new file mode 100644 index 0000000..57e6472 --- /dev/null +++ b/ModVersionChecker/ui/controls/DirectoryPickerControl.cs @@ -0,0 +1,58 @@ +using System; +using System.Windows.Forms; + +namespace ModVersionChecker.controls +{ + public class DirectoryPickerControl : UserControl + { + private readonly TextBox _textBox; + private readonly Button _browseButton; + + public string SelectedPath + { + get => _textBox.Text; + set => _textBox.Text = value; + } + + public DirectoryPickerControl() + { + _textBox = new TextBox + { + Width = 300, + ReadOnly = true, + Dock = DockStyle.Fill + }; + + _browseButton = new Button + { + Text = "Browse", + Width = 80, + Dock = DockStyle.Right + }; + + _browseButton.Click += (s, e) => + { + using var folderDialog = new FolderBrowserDialog(); + folderDialog.Description = "Select directory"; + if (folderDialog.ShowDialog() == DialogResult.OK) + { + SelectedPath = folderDialog.SelectedPath; + } + }; + + var panel = new TableLayoutPanel + { + ColumnCount = 2, + Dock = DockStyle.Fill, + AutoSize = true + }; + panel.ColumnStyles.Add(new ColumnStyle(SizeType.Percent, 80)); + panel.ColumnStyles.Add(new ColumnStyle(SizeType.Percent, 20)); + panel.Controls.Add(_textBox, 0, 0); + panel.Controls.Add(_browseButton, 1, 0); + + Controls.Add(panel); + AutoSize = true; + } + } +} \ No newline at end of file diff --git a/ModVersionChecker/ui/forms/AppDetailsForm.cs b/ModVersionChecker/ui/forms/AppDetailsForm.cs new file mode 100644 index 0000000..7a544f3 --- /dev/null +++ b/ModVersionChecker/ui/forms/AppDetailsForm.cs @@ -0,0 +1,74 @@ +using ModVersionChecker.managers.interfaces; +using ModVersionChecker.service.interfaces; + +namespace ModVersionChecker.ui.forms +{ + public class AppDetailsForm : Form + { + private TextBox _searchBox; + private ListBox _resultsList; + private Button _addButton; + private readonly IApiService _apiService; + private readonly IAppsManager _appsManager; + public App? SelectedApp { get; private set; } + + public event EventHandler OnAppAdded; + + public AppDetailsForm(IApiService apiService, IAppsManager appsManager) + { + _apiService = apiService ?? throw new ArgumentNullException(nameof(apiService)); + _appsManager = appsManager ?? throw new ArgumentNullException(nameof(appsManager)); + InitializeComponent(); + } + + private void InitializeComponent() + { + Text = "Search and Add App"; + Width = 400; + Height = 500; + Padding = new Padding(20); + + _searchBox = new TextBox { Dock = DockStyle.Top, PlaceholderText = "Search..." }; + _resultsList = new ListBox { Dock = DockStyle.Fill }; + _addButton = new Button { Text = "Add Selected", Dock = DockStyle.Bottom, Enabled = false }; + + _searchBox.TextChanged += async (s, e) => await UpdateResultsAync(); + _resultsList.SelectedIndexChanged += (s, e) => _addButton.Enabled = _resultsList.SelectedItem != null; + _resultsList.DoubleClick += (s, e) => AddSelected(); + _addButton.Click += (s, e) => AddSelected(); + + Controls.Add(_resultsList); + Controls.Add(_addButton); + Controls.Add(_searchBox); + } + + private async Task UpdateResultsAync() + { + var query = _searchBox.Text.Trim().ToLower(); + if (query.Length < 3) + { + return; + } + if (string.IsNullOrEmpty(query)) + { + _resultsList.DataSource = null; + return; + } + var results = await _apiService.SearchApps(query); + _resultsList.DataSource = results; + _resultsList.DisplayMember = "Name"; + } + + private void AddSelected() + { + if (_resultsList.SelectedItem is App app) + { + SelectedApp = app; + _appsManager.Insert(app); + DialogResult = DialogResult.OK; + OnAppAdded?.Invoke(this, "App saved"); + Close(); + } + } + } +} \ No newline at end of file diff --git a/ModVersionChecker/forms/AppDetailsForm.resx b/ModVersionChecker/ui/forms/AppDetailsForm.resx similarity index 100% rename from ModVersionChecker/forms/AppDetailsForm.resx rename to ModVersionChecker/ui/forms/AppDetailsForm.resx diff --git a/ModVersionChecker/ui/forms/FormFactory.cs b/ModVersionChecker/ui/forms/FormFactory.cs new file mode 100644 index 0000000..09b55f3 --- /dev/null +++ b/ModVersionChecker/ui/forms/FormFactory.cs @@ -0,0 +1,70 @@ +using Microsoft.Extensions.DependencyInjection; +using ModVersionChecker.managers.interfaces; +using ModVersionChecker.service.interfaces; + +namespace ModVersionChecker.ui.forms +{ + public class FormFactory : IFormFactory + { + private readonly IServiceProvider _serviceProvider; + + public FormFactory(IServiceProvider serviceProvider) + { + _serviceProvider = serviceProvider ?? throw new ArgumentNullException(nameof(serviceProvider)); + } + + public AppDetailsForm CreateAppDetailsForm(App? app, bool isEditable, EventHandler? onAppSaved) + { + var apiService = _serviceProvider.GetRequiredService(); + var appsManager = _serviceProvider.GetRequiredService(); + var form = new AppDetailsForm(apiService, appsManager); + if (onAppSaved != null) + { + form.OnAppAdded += onAppSaved; + } + return form; + } + + public GlobalConfigForm CreateGlobalConfigForm() + { + var configManager = _serviceProvider.GetRequiredService(); + return new GlobalConfigForm(configManager); + } + + public TypeConfigForm CreateTypeConfigForm() + { + var typeManager = _serviceProvider.GetRequiredService(); + var apiService = _serviceProvider.GetRequiredService(); + var configManager = _serviceProvider.GetRequiredService(); + return new TypeConfigForm(typeManager, apiService, configManager); + } + + //public SourcesConfigForm CreateSourcesConfigForm(EventHandler? onSourcesChanged) + //{ + // var sourcesDefManager = _serviceProvider.GetRequiredService(); + // var formFactory = _serviceProvider.GetRequiredService(); + // var form = new SourcesConfigForm(formFactory, sourcesDefManager); + // if (onSourcesChanged != null) + // { + // form.OnSourcesChanged += onSourcesChanged; + // } + // return form; + //} + + //public SourceDetailForm CreateSourceDetailForm(SourceDef? sourceDef, EventHandler? onSourceChanged) + //{ + // var sourcesDefManager = _serviceProvider.GetRequiredService(); + // var checkerTypesDefManager = _serviceProvider.GetRequiredService(); + // var formFactory = _serviceProvider.GetRequiredService(); + // var form = new SourceDetailForm(formFactory, sourcesDefManager); + // form.SourceDef = sourceDef; + + // if (onSourceChanged != null) + // { + // form.UpdateFields(); + // form.OnSourceChanged += onSourceChanged; + // } + // return form; + //} + } +} \ No newline at end of file diff --git a/ModVersionChecker/forms/GlobalConfigForm.cs b/ModVersionChecker/ui/forms/GlobalConfigForm.cs similarity index 94% rename from ModVersionChecker/forms/GlobalConfigForm.cs rename to ModVersionChecker/ui/forms/GlobalConfigForm.cs index 059856d..f096934 100644 --- a/ModVersionChecker/forms/GlobalConfigForm.cs +++ b/ModVersionChecker/ui/forms/GlobalConfigForm.cs @@ -1,12 +1,12 @@ -using ModVersionChecker.data.model; -using ModVersionChecker.managers.interfaces; +using ModVersionChecker.managers.interfaces; +using ModVersionChecker.model; -namespace ModVersionChecker.forms +namespace ModVersionChecker.ui.forms { public class GlobalConfigForm : Form { private IConfigManager _configManager; - private GlobalConfig _config; + private Config _config; private Label _millislabel, _checkStartupLabel, _runOnStartupLabel; private TrackBar _millisField; @@ -19,8 +19,17 @@ namespace ModVersionChecker.forms { _configManager = configManager; _config = _configManager.GetConfig(); - InitializeComponent(); + this.Load += GlobalConfigForm_Load; } + + private void GlobalConfigForm_Load(object sender, EventArgs e) + { + // Load existing configurations if needed + + InitializeComponent(); + + } + private void InitializeComponent() { SuspendLayout(); diff --git a/ModVersionChecker/ui/forms/IFormFactory.cs b/ModVersionChecker/ui/forms/IFormFactory.cs new file mode 100644 index 0000000..9a1ddfa --- /dev/null +++ b/ModVersionChecker/ui/forms/IFormFactory.cs @@ -0,0 +1,14 @@ + +namespace ModVersionChecker.ui.forms +{ + public interface IFormFactory + { + AppDetailsForm CreateAppDetailsForm(App? app, bool isEditable, EventHandler? onAppChanged); + GlobalConfigForm CreateGlobalConfigForm(); + TypeConfigForm CreateTypeConfigForm(); + + //SourcesConfigForm CreateSourcesConfigForm(EventHandler? onSourcesChanged); + + //SourceDetailForm CreateSourceDetailForm(SourceDef? sourceDef, EventHandler? onSourceChanged); + } +} \ No newline at end of file diff --git a/ModVersionChecker/forms/MainForm.cs b/ModVersionChecker/ui/forms/MainForm.cs similarity index 52% rename from ModVersionChecker/forms/MainForm.cs rename to ModVersionChecker/ui/forms/MainForm.cs index f1fdef4..dfda2a9 100644 --- a/ModVersionChecker/forms/MainForm.cs +++ b/ModVersionChecker/ui/forms/MainForm.cs @@ -1,43 +1,51 @@ -using ModVersionChecker.data.model; +using ModVersionChecker.enums; using ModVersionChecker.managers.interfaces; +using ModVersionChecker.model; +using ModVersionChecker.repository.api.dto; +using ModVersionChecker.service.interfaces; using ModVersionChecker.utils; -using System; -using System.Collections.Generic; -using System.Linq; -using System.Runtime.Intrinsics.Arm; -using System.Windows.Forms; -namespace ModVersionChecker.forms +namespace ModVersionChecker.ui.forms { public class MainForm : Form { - private readonly IConfigManager _configManager; private readonly IAppsManager _appsManager; private readonly IFormFactory _formFactory; - private readonly IAppStatusManager _appStatusManager; private readonly IFlightSimsManager _fsManager; + private readonly IApiService _apiService; + private readonly IVersionService _versionService; private readonly TableLayoutPanel _mainLayout; - private readonly GlobalConfig _globalConfig; - private List _apps = new List(); + private readonly Config _globalConfig; + private List _apps = new List(); private ListView _listView; private ImageList _statusImageList = new ImageList(); public event EventHandler OnConfigChanged; public event EventHandler OnRecheck; - private EventHandler onAppChangedHandler; + private EventHandler onAppSavedHandler; private MenuStrip _menuStrip; - private List _fsMods; + private List _fsMods; private readonly Dictionary _fsModPathTextBoxes = new Dictionary(); - public MainForm(IConfigManager configManager, IAppsManager appsManager, IFormFactory formFactory, IAppStatusManager appStatusManager, IFlightSimsManager fsManager) + private List _sources = new List(); + private List _typesDef = new List(); + + public MainForm( + IConfigManager configManager, + IAppsManager appsManager, + IFormFactory formFactory, + IFlightSimsManager fsManager, + IApiService apiService, + ITypeManager typeConfigManager, + IVersionService versionService) { - _configManager = configManager ?? throw new ArgumentNullException(nameof(configManager)); _appsManager = appsManager ?? throw new ArgumentNullException(nameof(appsManager)); _formFactory = formFactory ?? throw new ArgumentNullException(nameof(formFactory)); - _appStatusManager = appStatusManager ?? throw new ArgumentNullException(nameof(appStatusManager)); _fsManager = fsManager ?? throw new ArgumentNullException(nameof(fsManager)); + _apiService = apiService ?? throw new ArgumentNullException(nameof(apiService)); + _versionService = versionService ?? throw new ArgumentNullException(nameof(versionService)); _fsMods = _fsManager.Load(); _statusImageList.Images.Add("none", new Icon("Resources/ok-icon.ico")); @@ -48,26 +56,55 @@ namespace ModVersionChecker.forms Size = new Size(600, 800); StartPosition = FormStartPosition.CenterScreen; - _globalConfig = configManager.Load() ?? new GlobalConfig(); + _globalConfig = configManager.Load() ?? new Config(); _mainLayout = GetMainLayout(); - _mainLayout.Controls.Add(GetPathsPanel(), 0, 0); - _listView = GetListView(); _listView.SmallImageList = _statusImageList; - _mainLayout.Controls.Add(_listView , 0, 1); + _mainLayout.Controls.Add(_listView , 0, 0); - _mainLayout.Controls.Add(GetButtonsPanel(), 0, 2); + _mainLayout.Controls.Add(GetButtonsPanel(), 0, 1); - onAppChangedHandler = (s2, e) => + onAppSavedHandler = (s2, e) => { + var form = s2 as AppDetailsForm; + if (form == null || form.SelectedApp == null) return; + + var app = form.SelectedApp; + UpdateCurrentVersion(app); UpdateListView(); OnConfigChanged?.Invoke(this, EventArgs.Empty); }; - InitializeMenu(); + this.Load += MainForm_LoadAsync; + } + + private void UpdateCurrentVersion(App app) + { + TypeConfig? typeConfig = _globalConfig.Types.FirstOrDefault(tc => app.Type == tc.ShortName); + if (typeConfig == null) return; + var versionInDisk = VersionUtils.GetCurrentVersion(app, typeConfig); + + if (!string.IsNullOrEmpty(versionInDisk)) + { + app.CurrentVersion = versionInDisk; + app.LastCheckedAt = TimeUtils.GetUnixTimeMillis(DateTime.Now); + + _versionService.CheckApp(app); + } + + } + + + private async void MainForm_LoadAsync(object? sender, EventArgs e) + { + await _apiService.AuthenticateAsync("user", "user"); + _apps = await _apiService.GetAppsByIds([]); + _sources = await _apiService.GetSources(); + _typesDef = await _apiService.GetTypes(); + UpdateListView(); } private TableLayoutPanel GetMainLayout() @@ -76,50 +113,17 @@ namespace ModVersionChecker.forms var mainLayout = new TableLayoutPanel { Dock = DockStyle.Fill, - RowCount = 3, + RowCount = 2, ColumnCount = 1 }; - mainLayout.RowStyles.Add(new RowStyle(SizeType.Absolute, 150)); // Paths panel height + // mainLayout.RowStyles.Add(new RowStyle(SizeType.Absolute, 150)); // Paths panel height mainLayout.RowStyles.Add(new RowStyle(SizeType.Percent, 70)); // ListView takes remaining space mainLayout.RowStyles.Add(new RowStyle(SizeType.Absolute, 50)); // Button panel height Controls.Add(mainLayout); + InitializeMenu(); return mainLayout; } - private FlowLayoutPanel GetPathsPanel() - { - var pathPanel = new FlowLayoutPanel { Dock = DockStyle.Fill, FlowDirection = FlowDirection.TopDown }; - - foreach (var fsMod in _fsMods) - { - var singlePathPanel = new FlowLayoutPanel { FlowDirection = FlowDirection.LeftToRight, AutoSize = true }; - singlePathPanel.Controls.Add(new Label { Text = $"{fsMod.Name} Path:", Width = 100 }); - var pathField = new TextBox { Text = fsMod.Path ?? "", Width = 300 }; - singlePathPanel.Controls.Add(pathField); - _fsModPathTextBoxes.Add(fsMod.ShortName, pathField); - pathPanel.Controls.Add(singlePathPanel); - } - - //var pathPanel2024 = new FlowLayoutPanel { FlowDirection = FlowDirection.LeftToRight, AutoSize = true }; - //pathPanel2024.Controls.Add(new Label { Text = "MSFS 2024 Path:", Width = 100 }); - //var msfs2024PathField = new TextBox { Text = _globalConfig.FsModPaths.ContainsKey("msfs2024") ? _globalConfig.FsModPaths["msfs2024"].Path : "", Width = 300 }; - //pathPanel2024.Controls.Add(msfs2024PathField); - //pathPanel.Controls.Add(pathPanel2024); - - var savePathsButton = new Button { Text = "Save Paths" }; - savePathsButton.Click += (s, e) => - { - foreach (var fsMod in _fsMods) - { - fsMod.Path = _fsModPathTextBoxes[fsMod.ShortName].Text; - _fsManager.Save(fsMod); - } - _fsMods = _fsManager.Load(); - }; - pathPanel.Controls.Add(savePathsButton); - return pathPanel; - } - private ListView GetListView() { var listView = new ListView @@ -143,9 +147,9 @@ namespace ModVersionChecker.forms if (listView.SelectedItems.Count > 0) { ListViewItem selectedItem = listView.SelectedItems[0]; - AppConfig? app = selectedItem.Tag as AppConfig; + App? app = selectedItem.Tag as App; if (app == null) return; - if (_appStatusManager.GetAppStatus(app.Id) == AppStatus.UpdateAvailable) + if (app.Status == AppStatus.UPDATE_AVAILABLE) { if (string.IsNullOrEmpty(app.DownloadUrl)) { @@ -159,14 +163,7 @@ namespace ModVersionChecker.forms }); } else { - var form = _formFactory.CreateAppDetailsForm(app, true, onAppChangedHandler); - form.FormClosed += (s2, e) => - { - UpdateListView(); - }; - UpdateListView(); - form.ShowDialog(); - form.BringToFront(); + } } @@ -178,49 +175,38 @@ namespace ModVersionChecker.forms private FlowLayoutPanel GetButtonsPanel() { var buttonPanel = new FlowLayoutPanel { Dock = DockStyle.Fill }; var addButton = new Button { Text = "Add App" }; - var editButton = new Button { Text = "Edit App", Enabled = false }; var deleteButton = new Button { Text = "Delete App", Enabled = false }; var recheckButton = new Button { Text = "Recheck Versions" }; addButton.Click += (s, e) => { - var form = _formFactory.CreateAppDetailsForm(null, true, onAppChangedHandler); // Use factory + var form = _formFactory.CreateAppDetailsForm(null, true, onAppSavedHandler); // Use factory form.ShowDialog(); }; - editButton.Click += (s, e) => - { - if (_listView.SelectedItems.Count > 0) - { - ListViewItem selectedItem = _listView.SelectedItems[0]; - AppConfig app = selectedItem.Tag as AppConfig; - var form = _formFactory.CreateAppDetailsForm(app, true, onAppChangedHandler); // Use factory - form.ShowDialog(); - - } - }; deleteButton.Click += (s, e) => { if (_listView.SelectedItems.Count > 0 && _listView.SelectedItems[0].Tag != null) { - _appsManager.Delete((_listView.SelectedItems[0].Tag as AppConfig).Id); - UpdateListView(); - OnConfigChanged?.Invoke(this, EventArgs.Empty); + var app = _listView.SelectedItems[0].Tag as App; + DeleteApp(app); } }; _listView.SelectedIndexChanged += (s, e) => { - editButton.Enabled = deleteButton.Enabled = _listView.SelectedItems.Count > 0; + deleteButton.Enabled = _listView.SelectedItems.Count > 0; }; // Add recheck logic here recheckButton.Click += async (s, e) => { recheckButton.Enabled = false; - OnRecheck.Invoke(this, "User initiated recheck from ConfigForm"); + await _versionService.CheckAllApps(); + UpdateListView(); + //OnRecheck.Invoke(this, "User initiated recheck from ConfigForm"); recheckButton.Enabled = true; }; - buttonPanel.Controls.AddRange(new[] { addButton, editButton, deleteButton, recheckButton }); + buttonPanel.Controls.AddRange(new[] { addButton, deleteButton, recheckButton }); return buttonPanel; } @@ -229,14 +215,12 @@ namespace ModVersionChecker.forms _apps = _appsManager.Load(); _listView.Items.Clear(); foreach (var app in _apps) - { + { var item = new ListViewItem(app.Name); - item.Tag = app; - item.SubItems.Add(string.Join(", ", app.MsfsVersions)); - try - { - var fsMod = _fsMods.FirstOrDefault(fs => fs.ShortName == "msfs2024"); - // Pass the FsModPathConfig object directly, not its Path property + try { + item.Tag = app; + item.SubItems.Add(app.Type); + var currentVersion = app.CurrentVersion; var latestVersion = app.LatestVersion; var lastChecked = TimeUtils.ToFriendlyTime(app.LastCheckedAt); @@ -248,12 +232,13 @@ namespace ModVersionChecker.forms { item.SubItems.Add($"Error: {ex.Message}"); } - switch (_appStatusManager.GetAppStatus(app.Id)) + + switch (app.Status) { - case AppStatus.UpdateAvailable: + case AppStatus.UPDATE_AVAILABLE: item.ImageKey = "update"; break; - case AppStatus.Error: + case AppStatus.ERROR: item.ImageKey = "error"; break; default: @@ -261,8 +246,19 @@ namespace ModVersionChecker.forms break; } _listView.Items.Add(item); + + } + } + + private void DeleteApp(App app) + { + var result = MessageBox.Show($"Are you sure you want to delete '{app?.Name ?? ""}'?", "Confirm Delete", MessageBoxButtons.YesNo, MessageBoxIcon.Warning); + if (result == DialogResult.Yes) + { + _appsManager.Delete((_listView.SelectedItems[0].Tag as App).Id); + UpdateListView(); + OnConfigChanged?.Invoke(this, EventArgs.Empty); } - Console.WriteLine($"UpdateListView item count: {_listView.Items.Count}"); } private void InitializeMenu() @@ -273,18 +269,22 @@ namespace ModVersionChecker.forms var configMenu = new ToolStripMenuItem("Configuration"); // Add sub-menu items - var globalConfigItem = new ToolStripMenuItem("Global Settings"); - globalConfigItem.Click += (s, e) => ShowGlobalConfigDialog(); + var settingsItem = new ToolStripMenuItem("Settings"); + settingsItem.Click += (s, e) => ShowGlobalConfigDialog(); - var sourcesConfigItem = new ToolStripMenuItem("Sources"); - sourcesConfigItem.Click += (s, e) => ShowSourcesConfigDialog(); + var typesItem = new ToolStripMenuItem("Types"); + typesItem.Click += (s, e) => ShowTypesConfigForm(); - var FlightSimsConfigItem = new ToolStripMenuItem("Flight Sims"); - FlightSimsConfigItem.Click += (s, e) => MessageBox.Show("Flight Sims configuration dialog would open here."); + //var sourcesConfigItem = new ToolStripMenuItem("Sources"); + //sourcesConfigItem.Click += (s, e) => ShowSourcesConfigDialog(); - configMenu.DropDownItems.Add(globalConfigItem); - configMenu.DropDownItems.Add(sourcesConfigItem); - configMenu.DropDownItems.Add(FlightSimsConfigItem); + //var FlightSimsConfigItem = new ToolStripMenuItem("Flight Sims"); + //FlightSimsConfigItem.Click += (s, e) => MessageBox.Show("Flight Sims configuration dialog would open here."); + + configMenu.DropDownItems.Add(settingsItem); + configMenu.DropDownItems.Add(typesItem); + // configMenu.DropDownItems.Add(sourcesConfigItem); + // configMenu.DropDownItems.Add(FlightSimsConfigItem); _menuStrip.Items.Add(configMenu); @@ -293,6 +293,12 @@ namespace ModVersionChecker.forms MainMenuStrip = _menuStrip; } + private void ShowTypesConfigForm() + { + var typesConfigForm = _formFactory.CreateTypeConfigForm(); + typesConfigForm.ShowDialog(); + } + private void ShowGlobalConfigDialog() { // Show your global config form/dialog here @@ -300,11 +306,11 @@ namespace ModVersionChecker.forms globalConfigForm.ShowDialog(); } - private void ShowSourcesConfigDialog() - { - EventHandler onSourcesChanged = (s, e) => MessageBox.Show("Sources Changed"); - var form = _formFactory.CreateSourcesConfigForm(onSourcesChanged); - form.ShowDialog(); - } + //private void ShowSourcesConfigDialog() + //{ + // EventHandler onSourcesChanged = (s, e) => MessageBox.Show("Sources Changed"); + // var form = _formFactory.CreateSourcesConfigForm(onSourcesChanged); + // form.ShowDialog(); + //} } } \ No newline at end of file diff --git a/ModVersionChecker/forms/MainForm.resx b/ModVersionChecker/ui/forms/MainForm.resx similarity index 100% rename from ModVersionChecker/forms/MainForm.resx rename to ModVersionChecker/ui/forms/MainForm.resx diff --git a/ModVersionChecker/ui/forms/TypeConfigForm.cs b/ModVersionChecker/ui/forms/TypeConfigForm.cs new file mode 100644 index 0000000..cb3eacd --- /dev/null +++ b/ModVersionChecker/ui/forms/TypeConfigForm.cs @@ -0,0 +1,145 @@ +using ModVersionChecker.controls; +using ModVersionChecker.managers.interfaces; +using ModVersionChecker.model; +using ModVersionChecker.repository.api.dto; +using ModVersionChecker.service.interfaces; +using ModVersionChecker.utils; + +namespace ModVersionChecker.ui.forms +{ + public class TypeConfigForm : Form + { + private List _typeDefs; + private List _typeConfigs; + private Config _globalConfig; + + private readonly Dictionary _typeCheckBoxes = new(); + private readonly Dictionary _typePanels = new(); + private readonly Dictionary<(string typeId, string fieldName), Control> _fieldControls = new(); + private FlowLayoutPanel _mainPanel; + private Button _saveButton, _cancelButton; + private readonly IApiService _apiService; + private readonly IConfigManager _configManager; + private readonly ITypeManager _typeManager; + + public TypeConfigForm( + ITypeManager typeManager, + IApiService apiService, + IConfigManager configManager + ) + { + _apiService = apiService ?? throw new ArgumentNullException(nameof(apiService)); + _configManager = configManager ?? throw new ArgumentNullException(nameof(configManager)); + _typeManager = typeManager ?? throw new ArgumentNullException(nameof(typeManager)); + Load += TypeConfigForm_LoadAsync; + + } + + public async void TypeConfigForm_LoadAsync(object sender, EventArgs e) + { + _typeDefs = await _apiService.GetTypes(); + _typeConfigs = _typeManager.GetTypeConfigs(); + _globalConfig = _configManager.Load(); + InitializeComponent(); + } + + private void InitializeComponent() + { + Text = "Configure Types"; + Width = 600; + Height = 700; + StartPosition = FormStartPosition.CenterParent; + _mainPanel = new FlowLayoutPanel { Dock = DockStyle.Top, AutoScroll = true, FlowDirection = FlowDirection.TopDown, WrapContents = false, Width = 580, Height = 600 }; + + foreach (var typeDef in _typeDefs) + { + var isInConfig = _globalConfig.Types.Any(t => t.Id == typeDef.Id); + var typeConfig = _globalConfig.Types.FirstOrDefault(tc => tc.Id == typeDef.Id); + var checkBox = new CheckBox { + Text = typeDef.Name, + Checked = isInConfig, + Enabled = !isInConfig, + AutoSize = true + }; + _typeCheckBoxes[typeDef.Id] = checkBox; + var panel = new Panel { + Visible = checkBox.Checked, + AutoSize = false, + BorderStyle = BorderStyle.FixedSingle, + Width = _mainPanel.Width - 20, // leave some margin + Height = 0 // will be set by content + }; + _typePanels[typeDef.Id] = panel; + + checkBox.CheckedChanged += (s, e) => { panel.Visible = checkBox.Checked; }; + + var fieldsLayout = new TableLayoutPanel { + ColumnCount = 2, + AutoSize = true, + Dock = DockStyle.Top + }; + foreach (var field in typeDef.ConfigFields) + { + fieldsLayout.RowCount++; + fieldsLayout.RowStyles.Add(new RowStyle(SizeType.AutoSize)); + fieldsLayout.Controls.Add(new Label { + Text = $"{field.Label ?? field.Name} {field.ControlType}", + AutoSize = true + }, 0, fieldsLayout.RowCount - 1); + + + + var filedInput = InputControlsFactory.CreateControl(field, typeConfig, !isInConfig); + _fieldControls[(typeDef.Id, field.Name)] = filedInput; + fieldsLayout.Controls.Add(filedInput, 1, fieldsLayout.RowCount - 1); + } + panel.Controls.Add(fieldsLayout); + panel.Height = fieldsLayout.PreferredSize.Height + 10; + _mainPanel.Controls.Add(checkBox); + _mainPanel.Controls.Add(panel); + } + + _saveButton = new Button { Text = "Save", AutoSize = true }; + _saveButton.Click += (s, e) => { SaveConfigs(); }; + _cancelButton = new Button { Text = "Cancel", AutoSize = true }; + _cancelButton.Click += (s, e) => { DialogResult = DialogResult.Cancel; Close(); }; + var buttonPanel = new FlowLayoutPanel { FlowDirection = FlowDirection.RightToLeft, Dock = DockStyle.Bottom, AutoSize = true }; + buttonPanel.Controls.Add(_saveButton); + buttonPanel.Controls.Add(_cancelButton); + + Controls.Add(_mainPanel); + Controls.Add(buttonPanel); + } + + private void SaveConfigs() + { + var result = new List(); + foreach (var typeDef in _typeDefs) + { + if (_typeCheckBoxes[typeDef.Id].Checked) + { + var config = new TypeConfig { Id = typeDef.Id, Name = typeDef.Name, ShortName = typeDef.ShortName, ConfigValues = new Dictionary() }; + foreach (var field in typeDef.ConfigFields) + { + if (_fieldControls.TryGetValue((typeDef.Id, field.Name), out var ctrl)) + { + if (ctrl is DirectoryPickerControl) + { + config.ConfigValues[field.Name] = (ctrl as DirectoryPickerControl).SelectedPath; + } + else + { + config.ConfigValues[field.Name] = ctrl.Text; + } + } + } + result.Add(config); + } + } + _globalConfig.Types = result; + _configManager.Save(_globalConfig); + DialogResult = DialogResult.OK; + Close(); + } + } +} diff --git a/ModVersionChecker/utils/InputControlsFactory.cs b/ModVersionChecker/utils/InputControlsFactory.cs new file mode 100644 index 0000000..fce3896 --- /dev/null +++ b/ModVersionChecker/utils/InputControlsFactory.cs @@ -0,0 +1,64 @@ +using ModVersionChecker.controls; +using ModVersionChecker.model; +using ModVersionChecker.repository.api.dto; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace ModVersionChecker.utils +{ + public class InputControlsFactory + { + public static Control CreateControl(FieldResponse field, TypeConfig config, Boolean enabled = true) + { + var value = config?.ConfigValues != null && config.ConfigValues.ContainsKey(field.Name) ? config.ConfigValues[field.Name] : field.DefaultValue; + var directoryField = () => + { + var textBox = new TextBox + { + Width = 300, + Text = value, + ReadOnly = true + }; + var control = new TableLayoutPanel + { + AutoSize = true, + Dock = DockStyle.Fill, + ColumnCount = 2, + RowCount = 1, + ColumnStyles = { new ColumnStyle(SizeType.Percent, 80), new ColumnStyle(SizeType.Percent, 20) } + }; + control.Controls.Add(textBox, 0, 0); + var browseButton = new Button { Text = "Browse", Width = 80, Enabled = enabled }; + browseButton.Click += (s, e) => + { + using (var folderDialog = new FolderBrowserDialog()) + { + folderDialog.Description = $"Select directory for {field.Label}"; + //folderDialog.SelectedPath = textBox.Text == "" ? Path.Combine(fs.Path) : textBox.Text; + + if (folderDialog.ShowDialog() == DialogResult.OK) + { + string selectedDirectory = folderDialog.SelectedPath; + //string folderName = Path.GetFileName(selectedDirectory); + textBox.Text = selectedDirectory; + } + } + }; + control.Controls.Add(browseButton, 1, 0); + return control; + }; + + return field. ControlType.ToLower() switch + { + "text" => new TextBox { Text = value, Width = 300, Enabled = enabled }, + "number" => new NumericUpDown { Value = decimal.TryParse(value, out var num) ? num : 0, Width = 100, Enabled = enabled }, + "checkbox" => new CheckBox { Checked = bool.TryParse(value, out var isChecked) && isChecked, AutoSize = true, Enabled = enabled }, + "directory" => new DirectoryPickerControl { SelectedPath = value, Enabled = enabled }, + _ => new TextBox { Text = value, Width = 300, Enabled = enabled } + }; + } + } +} diff --git a/ModVersionChecker/utils/TimeUtils.cs b/ModVersionChecker/utils/TimeUtils.cs index da1797a..deeaf01 100644 --- a/ModVersionChecker/utils/TimeUtils.cs +++ b/ModVersionChecker/utils/TimeUtils.cs @@ -8,8 +8,10 @@ namespace ModVersionChecker.utils { public class TimeUtils { - public static long GetUnixTimeMillis(DateTime? dateTime) + public static long GetUnixTimeMillis(DateTime? dateTime = null) { + var a = DateTime.Now; + var b = DateTime.UtcNow; DateTime dt = dateTime ?? DateTime.UtcNow; return (long)(dt - new DateTime(1970, 1, 1)).TotalMilliseconds; } diff --git a/ModVersionChecker/utils/VersionUtils.cs b/ModVersionChecker/utils/VersionUtils.cs index 5ec1117..f28ab48 100644 --- a/ModVersionChecker/utils/VersionUtils.cs +++ b/ModVersionChecker/utils/VersionUtils.cs @@ -1,4 +1,4 @@ -using ModVersionChecker.data.model; +using ModVersionChecker.model; using System.Text.Json; using System.Text.RegularExpressions; @@ -6,14 +6,14 @@ namespace ModVersionChecker.utils { public static class VersionUtils { - public static string GetCurrentVersion(AppConfig app, FsModPathConfig config) + public static string GetCurrentVersion(App app, TypeConfig typeConfig) { - var versionConfig = app.FsFields; - var packageName = versionConfig["msfs2024"]["package"]; - var fsPath = config.Path; - var fsFile = config.File; - var fsFileType = config.FileType; - var fsKey = config.Key; + var versionConfig = app.Fields; + var packageName = versionConfig["package"]; + var fsPath = typeConfig.ConfigValues.GetValueOrDefault("path", ""); + var fsFile = typeConfig.ConfigValues.GetValueOrDefault("filename"); + var fsFileType = typeConfig.ConfigValues.GetValueOrDefault("filetype", ""); + var fsKey = typeConfig.ConfigValues.GetValueOrDefault("jsonkey", ""); var filePath = Path.GetFullPath(Path.Combine(fsPath, packageName, fsFile));