diff --git a/.gitignore b/.gitignore index c25fee1..507d8b4 100644 --- a/.gitignore +++ b/.gitignore @@ -9,6 +9,7 @@ [Ll]og/ [Ll]ogs/ [Pp]ublish/ +data/ # IDE files .vs/ diff --git a/ParadoxSaveParser.Lib/ParadoxSaveParser.Lib.csproj b/ParadoxSaveParser.Lib/ParadoxSaveParser.Lib.csproj index 9c490a9..595335a 100644 --- a/ParadoxSaveParser.Lib/ParadoxSaveParser.Lib.csproj +++ b/ParadoxSaveParser.Lib/ParadoxSaveParser.Lib.csproj @@ -6,8 +6,4 @@ enable - - - - diff --git a/ParadoxSaveParser.WebAPI/PathHelper.cs b/ParadoxSaveParser.WebAPI/PathHelper.cs index ad0ccc1..e853ca3 100644 --- a/ParadoxSaveParser.WebAPI/PathHelper.cs +++ b/ParadoxSaveParser.WebAPI/PathHelper.cs @@ -4,9 +4,9 @@ namespace ParadoxSaveParser.WebAPI; public static class PathHelper { - public const string DATA_DIR = "data"; public static string SAVES_DIR = Path.Join(DATA_DIR, "saves"); public static string GetMetaFilePath(string save_id) => Path.Join(SAVES_DIR, save_id + ".meta.json"); public static string GetEU4SaveFilePath(string save_id) => Path.Join(SAVES_DIR, save_id + ".eu4"); + public static string GetParsedSaveFilePath(string save_id) => Path.Join(SAVES_DIR, save_id + ".parsed.json"); } \ No newline at end of file diff --git a/ParadoxSaveParser.WebAPI/Program.cs b/ParadoxSaveParser.WebAPI/Program.cs index f78f030..a30e99f 100644 --- a/ParadoxSaveParser.WebAPI/Program.cs +++ b/ParadoxSaveParser.WebAPI/Program.cs @@ -1,13 +1,14 @@ global using System; +global using System.IO; +global using System.Text.Json; +global using System.Threading.Tasks; +global using DTLib.Demystifier; +global using ParadoxSaveParser.Lib; using System.Collections.Concurrent; -using System.IO; using System.Linq; -using System.Text.Json; -using DTLib.Demystifier; using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Http; using Microsoft.Extensions.Logging; -using ParadoxSaveParser.Lib; namespace ParadoxSaveParser.WebAPI; @@ -21,6 +22,8 @@ public class Program var builder = WebApplication.CreateBuilder(args); _app = builder.Build(); + Directory.CreateDirectory(PathHelper.DATA_DIR); + Directory.CreateDirectory(PathHelper.SAVES_DIR); foreach (var metaFilePath in Directory.GetFiles(PathHelper.SAVES_DIR, "*.meta.json", SearchOption.TopDirectoryOnly)) { using var metaFile = File.Open(metaFilePath, FileMode.Open, FileAccess.Read); @@ -35,40 +38,53 @@ public class Program } _app.UseHttpsRedirection(); - _app.MapPost("/parse/eu4", (HttpContext httpContext) => - { - var remoteFile = httpContext.Request.Form.Files.FirstOrDefault(); - if(remoteFile is null || !remoteFile.FileName.EndsWith(".eu4")) - throw new Exception($"Invalid file format: {remoteFile?.FileName}"); - - string saveId = Guid.NewGuid().ToString(); - string metaFilePath = PathHelper.GetMetaFilePath(saveId); - if (File.Exists(metaFilePath)) - { - httpContext.Response.StatusCode = StatusCodes.Status500InternalServerError; - throw new Exception($"Guid collision! file {metaFilePath} already exists."); - } - - var meta = new SaveFileMetadata - { - id = saveId, - game = Game.EU4, - status = SaveFileProcessingStatus.Initialized, - }; - if (!_saveMetadataStorage.TryAdd(saveId, meta)) - { - throw new Exception($"Guid collision! Can't create metadata with id {saveId}"); - } - - BeginEU4SaveParse(remoteFile, meta); - return meta; - }); - + _app.MapGet("/getSaveStatus", GetSaveStatusHandler); + _app.MapPost("/parseSave/eu4", ParseSaveEU4Handler); _app.Run(); } - private static async void BeginEU4SaveParse(IFormFile remoteFile, SaveFileMetadata meta) + private static async Task GetSaveStatusHandler(HttpContext httpContext) { + httpContext.Request.Query.TryGetValue("id", out var ids); + string? id = ids.FirstOrDefault(); + if (string.IsNullOrEmpty(id)) + { + throw new BadHttpRequestException("No id provided", + StatusCodes.Status400BadRequest); + } + + if (!_saveMetadataStorage.TryGetValue(id, out var meta)) + { + throw new BadHttpRequestException($"Save with {id} not found", + StatusCodes.Status400BadRequest); + } + + await httpContext.Response.WriteAsJsonAsync(meta); + } + + private static async Task ParseSaveEU4Handler(HttpContext httpContext) + { + var remoteFile = httpContext.Request.Form.Files.FirstOrDefault(); + if (remoteFile is null || !remoteFile.FileName.EndsWith(".eu4")) + { + throw new BadHttpRequestException($"Invalid file format: {remoteFile?.FileName}", + StatusCodes.Status400BadRequest); + } + + string saveId = Guid.NewGuid().ToString(); + string metaFilePath = PathHelper.GetMetaFilePath(saveId); + if (File.Exists(metaFilePath)) + { + httpContext.Response.StatusCode = StatusCodes.Status500InternalServerError; + throw new BadHttpRequestException($"Guid collision! file {metaFilePath} already exists.", StatusCodes.Status500InternalServerError); + } + + var meta = new SaveFileMetadata { id = saveId, game = Game.EU4, status = SaveFileProcessingStatus.Initialized, }; + if (!_saveMetadataStorage.TryAdd(saveId, meta)) + { + throw new BadHttpRequestException($"Guid collision! Can't create metadata with id {saveId}", StatusCodes.Status500InternalServerError); + } + try { meta.status = SaveFileProcessingStatus.Uploading; @@ -76,22 +92,29 @@ public class Program await using var saveFile = File.Open(saveFilePath, FileMode.CreateNew, FileAccess.ReadWrite); await using (var remoteStream = remoteFile.OpenReadStream()) { + await Task.Delay(50000); await remoteStream.CopyToAsync(saveFile); } meta.status = SaveFileProcessingStatus.Parsing; saveFile.Seek(0, SeekOrigin.Begin); var parser = new ParserEU4(saveFile); - //var result = parser.Parse(); + var result = parser.Parse(); meta.status = SaveFileProcessingStatus.SavingResults; - // save + string resultFilePath = PathHelper.GetParsedSaveFilePath(meta.id); + await using var resultFile = File.Open(resultFilePath, FileMode.CreateNew, FileAccess.Write); + await JsonSerializer.SerializeAsync(resultFile, result); meta.status = SaveFileProcessingStatus.Done; + meta.SaveToFile(); } catch (Exception ex) { meta.status = SaveFileProcessingStatus.Error; string errorMesage = ex.ToStringDemystified(); + meta.errorMesage = errorMesage; _app.Logger.Log(LogLevel.Error, "EU4SaveParse Error: {errorMesage}", errorMesage); } + + await httpContext.Response.WriteAsJsonAsync(meta); } } \ No newline at end of file diff --git a/ParadoxSaveParser.WebAPI/Properties/launchSettings.json b/ParadoxSaveParser.WebAPI/Properties/launchSettings.json index 44a68c4..360984c 100644 --- a/ParadoxSaveParser.WebAPI/Properties/launchSettings.json +++ b/ParadoxSaveParser.WebAPI/Properties/launchSettings.json @@ -12,8 +12,6 @@ "http": { "commandName": "Project", "dotnetRunMessages": true, - "launchBrowser": true, - "launchUrl": "/", "applicationUrl": "http://localhost:5226", "environmentVariables": { "ASPNETCORE_ENVIRONMENT": "Development" @@ -22,8 +20,6 @@ "https": { "commandName": "Project", "dotnetRunMessages": true, - "launchBrowser": true, - "launchUrl": "/", "applicationUrl": "https://localhost:7032;http://localhost:5226", "environmentVariables": { "ASPNETCORE_ENVIRONMENT": "Development" @@ -31,8 +27,6 @@ }, "IIS Express": { "commandName": "IISExpress", - "launchBrowser": true, - "launchUrl": "/", "environmentVariables": { "ASPNETCORE_ENVIRONMENT": "Development" } diff --git a/ParadoxSaveParser.WebAPI/SaveFileMetadata.cs b/ParadoxSaveParser.WebAPI/SaveFileMetadata.cs index e21eb37..13a534b 100644 --- a/ParadoxSaveParser.WebAPI/SaveFileMetadata.cs +++ b/ParadoxSaveParser.WebAPI/SaveFileMetadata.cs @@ -1,5 +1,6 @@ using System.IO; using System.Text.Json; +using System.Text.Json.Serialization; namespace ParadoxSaveParser.WebAPI; @@ -16,9 +17,15 @@ public enum Game public class SaveFileMetadata { public required string id { get; init; } + + [JsonConverter(typeof(JsonStringEnumConverter))] public required Game game { get; init; } + + [JsonConverter(typeof(JsonStringEnumConverter))] public required SaveFileProcessingStatus status { get; set; } + + [JsonIgnore] public string? errorMesage { get; set; } private static readonly JsonSerializerOptions _jsonOptions = new() { WriteIndented = true }; diff --git a/ParadoxSaveParser.sln b/ParadoxSaveParser.sln index 8f93e34..fbc158a 100644 --- a/ParadoxSaveParser.sln +++ b/ParadoxSaveParser.sln @@ -4,6 +4,11 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ParadoxSaveParser.WebAPI", EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ParadoxSaveParser.Lib", "ParadoxSaveParser.Lib\ParadoxSaveParser.Lib.csproj", "{53ED0135-9513-4DE2-9187-CF2899F179B3}" EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "SolutionFolder", "SolutionFolder", "{F1D312F1-0620-4E35-8D78-9A2808CDE12C}" + ProjectSection(SolutionItems) = preProject + .gitignore = .gitignore + EndProjectSection +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU