diff --git a/.gitignore b/.gitignore index 507d8b4..dd2ca48 100644 --- a/.gitignore +++ b/.gitignore @@ -10,6 +10,7 @@ [Ll]ogs/ [Pp]ublish/ data/ +config.dtsod # IDE files .vs/ diff --git a/ParadoxSaveParser.WebAPI/Config.cs b/ParadoxSaveParser.WebAPI/Config.cs new file mode 100644 index 0000000..12fba0b --- /dev/null +++ b/ParadoxSaveParser.WebAPI/Config.cs @@ -0,0 +1,34 @@ +using DTLib.Dtsod; + +namespace ParadoxSaveParser.WebAPI; + +public class Config +{ + public const int ActualVersion = 1; + + public int Version = ActualVersion; + public string BaseUrl = "http://127.0.0.1:8080/"; + + public static Config FromDtsod(DtsodV23 d) + { + var cfg = new Config + { + Version = d["version"], + BaseUrl = d["baseUrl"] + }; + if (cfg.Version < ActualVersion) + throw new Exception($"config is obsolete (config v{cfg.Version} < program v{ActualVersion})"); + if(cfg.Version > ActualVersion) + throw new Exception($"program is obsolete (config v{cfg.Version} > program v{ActualVersion})"); + return cfg; + } + + public DtsodV23 ToDtsod() => + new() + { + { "version", Version }, + { "baseUrl", BaseUrl }, + }; + + public override string ToString() => ToDtsod().ToString(); +} \ No newline at end of file diff --git a/ParadoxSaveParser.WebAPI/ParadoxSaveParser.WebAPI.csproj b/ParadoxSaveParser.WebAPI/ParadoxSaveParser.WebAPI.csproj index 60e5306..7a8b9f4 100644 --- a/ParadoxSaveParser.WebAPI/ParadoxSaveParser.WebAPI.csproj +++ b/ParadoxSaveParser.WebAPI/ParadoxSaveParser.WebAPI.csproj @@ -1,6 +1,8 @@ - + + Exe net8.0 + latest enable disable true @@ -11,7 +13,7 @@ - + @@ -28,5 +30,6 @@ + diff --git a/ParadoxSaveParser.WebAPI/PathHelper.cs b/ParadoxSaveParser.WebAPI/PathHelper.cs index 968b69a..8f5a93d 100644 --- a/ParadoxSaveParser.WebAPI/PathHelper.cs +++ b/ParadoxSaveParser.WebAPI/PathHelper.cs @@ -1,12 +1,10 @@ -using System.IO; - -namespace ParadoxSaveParser.WebAPI; +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 GetSaveFilePath(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"); + public static readonly IOPath DATA_DIR = "data"; + public static readonly IOPath SAVES_DIR = Path.Concat(DATA_DIR, "saves"); + public static IOPath GetMetaFilePath(string save_id) => Path.Concat(SAVES_DIR, save_id + ".meta.json"); + public static IOPath GetSaveFilePath(string save_id) => Path.Concat(SAVES_DIR, save_id + ".eu4"); + public static IOPath GetParsedSaveFilePath(string save_id) => Path.Concat(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 9dce370..f540d3d 100644 --- a/ParadoxSaveParser.WebAPI/Program.cs +++ b/ParadoxSaveParser.WebAPI/Program.cs @@ -1,25 +1,36 @@ global using System; -global using System.IO; global using System.Collections.Generic; global using System.Text; global using System.Text.Json; +global using System.Threading; global using System.Threading.Tasks; +global using DTLib; global using DTLib.Demystifier; +global using DTLib.Filesystem; +global using DTLib.Logging; global using ParadoxSaveParser.Lib; +global using Directory = DTLib.Filesystem.Directory; +global using File = DTLib.Filesystem.File; +global using Path = DTLib.Filesystem.Path; using System.Collections.Concurrent; +using System.IO; using System.IO.Compression; using System.Linq; +using System.Net; using System.Text.Encodings.Web; -using Microsoft.AspNetCore.Builder; -using Microsoft.AspNetCore.Http; -using Microsoft.Extensions.Logging; +using DTLib.Dtsod; +using DTLib.Extensions; +using DTLib.Web; +using DTLib.Web.Routes; namespace ParadoxSaveParser.WebAPI; public class Program { + private static readonly IOPath _configPath = "./config.dtsod"; + private static Config _config = new(); + private static readonly ILogger _loggerRoot = new ConsoleLogger(); private static ConcurrentDictionary _saveMetadataStorage = new(); - private static WebApplication _app = null!; private static JsonSerializerOptions _saveSerializerOptions = new() { @@ -30,31 +41,85 @@ public class Program public static void Main(string[] args) { - 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)) + Console.InputEncoding = Encoding.UTF8; + Console.OutputEncoding = Encoding.UTF8; + ContextLogger logger = new ContextLogger(nameof(Main), _loggerRoot); + CancellationTokenSource mainCancel = new(); + Console.CancelKeyPress += (_, _) => { - using var metaFile = File.Open(metaFilePath, FileMode.Open, FileAccess.Read); + logger.LogInfo("stopping server..."); + mainCancel.Cancel(); + }; + + try + { + // config + if (!File.Exists(_configPath)) + { + logger.LogWarn("config file not found."); + File.WriteAllText(_configPath, _config.ToString()); + logger.LogWarn($"created default at {_configPath}."); + } + else _config = Config.FromDtsod(new DtsodV23(File.ReadAllText(_configPath))); + + PrepareLocalFiles(); + + // http server + var router = new SimpleRouter(_loggerRoot); + router.DefaultRoute = new ServeFilesRouteHandler("public"); + router.MapRoute("/aaa", async ctx => + { + byte[] buffer = + """ + + + +

aaa

+ + + """.ToBytes(); + ctx.Response.ContentLength64 = buffer.Length; + await ctx.Response.OutputStream.WriteAsync(buffer); + return HttpStatusCode.OK; + }); + // router.MapGet("/getSaveStatus", GetSaveStatusHandler); + // router.MapPost("/uploadSave/eu4", UploadSaveHandler); + // app.MapPost("/parseSave/eu4", ParseSaveEU4Handler); + + var app = new WebApp(_config.BaseUrl, _loggerRoot, router, mainCancel.Token); + app.Run().GetAwaiter().GetResult(); + } + catch (OperationCanceledException ex) + { + logger.LogWarn($"catched OperationCanceledException from {ex.Source}"); + } + catch (Exception ex) + { + logger.LogError(ex.ToStringDemystified()); + } + } + + public static void PrepareLocalFiles() + { + Directory.Create(PathHelper.DATA_DIR); + Directory.Create(PathHelper.SAVES_DIR); + foreach (var metaFilePath in System.IO.Directory.GetFiles( + PathHelper.SAVES_DIR.Str, "*.meta.json", + SearchOption.TopDirectoryOnly)) + { + using var metaFile = File.OpenRead(metaFilePath); var meta = JsonSerializer.Deserialize(metaFile) ?? throw new NullReferenceException(metaFilePath); if (meta.status != SaveFileProcessingStatus.Done) { - _app.Logger.Log(LogLevel.Warning, "metadata file '{metaFilePath}' status has invalid status {status}", metaFilePath, meta.status); + _loggerRoot.LogWarn(nameof(PrepareLocalFiles), $"metadata file '{metaFilePath}' status has invalid status {meta.status}"); } if(!_saveMetadataStorage.TryAdd(meta.id, meta)) throw new Exception("Guid collision!"); } - - _app.UseHttpsRedirection(); - _app.MapGet("/getSaveStatus", GetSaveStatusHandler); - _app.MapPost("/uploadSave/eu4", UploadSaveHandler); - _app.MapPost("/parseSave/eu4", ParseSaveEU4Handler); - _app.Run(); } - + + /* private static async Task UploadSaveHandler(HttpContext httpContext) { var remoteFile = httpContext.Request.Form.Files.FirstOrDefault(); @@ -65,7 +130,7 @@ public class Program } string saveId = Guid.NewGuid().ToString(); - string metaFilePath = PathHelper.GetMetaFilePath(saveId); + IOPath metaFilePath = PathHelper.GetMetaFilePath(saveId); if (File.Exists(metaFilePath)) { httpContext.Response.StatusCode = StatusCodes.Status500InternalServerError; @@ -79,8 +144,8 @@ public class Program } meta.status = SaveFileProcessingStatus.Uploading; - string saveFilePath = PathHelper.GetSaveFilePath(meta.id); - await using var saveFile = File.Open(saveFilePath, FileMode.CreateNew, FileAccess.ReadWrite); + IOPath saveFilePath = PathHelper.GetSaveFilePath(meta.id); + await using var saveFile = File.OpenWrite(saveFilePath); await using var remoteStream = remoteFile.OpenReadStream(); await remoteStream.CopyToAsync(saveFile); meta.status = SaveFileProcessingStatus.Uploaded; @@ -128,21 +193,21 @@ public class Program if (meta.status != SaveFileProcessingStatus.Uploaded) throw new Exception($"Invalid save processing status: {meta.status}"); - using var zipArchive = ZipFile.Open(PathHelper.GetSaveFilePath(meta.id), ZipArchiveMode.Read); + using var zipArchive = ZipFile.Open(PathHelper.GetSaveFilePath(meta.id).Str, ZipArchiveMode.Read); var zipEntry = zipArchive.Entries.FirstOrDefault(e => e.Name == "gamestate"); if (zipEntry is null) throw new Exception("Invalid save format: no gamestate file found"); string extractedGamestatePath = PathHelper.GetSaveFilePath(meta.id) + ".gamestate"; zipEntry.ExtractToFile(extractedGamestatePath); - var gamestateStream = File.Open(extractedGamestatePath, FileMode.Open, FileAccess.Read); + var gamestateStream = File.OpenRead(extractedGamestatePath); meta.status = SaveFileProcessingStatus.Parsing; var parser = new Parser(gamestateStream); var result = parser.Parse(); meta.status = SaveFileProcessingStatus.SavingResults; - string resultFilePath = PathHelper.GetParsedSaveFilePath(meta.id); - await using var resultFile = File.Open(resultFilePath, FileMode.CreateNew, FileAccess.Write); + IOPath resultFilePath = PathHelper.GetParsedSaveFilePath(meta.id); + await using var resultFile = File.OpenWrite(resultFilePath); await JsonSerializer.SerializeAsync(resultFile, result, _saveSerializerOptions); meta.status = SaveFileProcessingStatus.Done; meta.SaveToFile(); @@ -158,4 +223,5 @@ public class Program 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 deleted file mode 100644 index 360984c..0000000 --- a/ParadoxSaveParser.WebAPI/Properties/launchSettings.json +++ /dev/null @@ -1,35 +0,0 @@ -{ - "$schema": "http://json.schemastore.org/launchsettings.json", - "iisSettings": { - "windowsAuthentication": false, - "anonymousAuthentication": true, - "iisExpress": { - "applicationUrl": "http://localhost:38396", - "sslPort": 44312 - } - }, - "profiles": { - "http": { - "commandName": "Project", - "dotnetRunMessages": true, - "applicationUrl": "http://localhost:5226", - "environmentVariables": { - "ASPNETCORE_ENVIRONMENT": "Development" - } - }, - "https": { - "commandName": "Project", - "dotnetRunMessages": true, - "applicationUrl": "https://localhost:7032;http://localhost:5226", - "environmentVariables": { - "ASPNETCORE_ENVIRONMENT": "Development" - } - }, - "IIS Express": { - "commandName": "IISExpress", - "environmentVariables": { - "ASPNETCORE_ENVIRONMENT": "Development" - } - } - } -} diff --git a/ParadoxSaveParser.WebAPI/SaveFileMetadata.cs b/ParadoxSaveParser.WebAPI/SaveFileMetadata.cs index 1bb3edc..1526e0b 100644 --- a/ParadoxSaveParser.WebAPI/SaveFileMetadata.cs +++ b/ParadoxSaveParser.WebAPI/SaveFileMetadata.cs @@ -1,6 +1,4 @@ -using System.IO; -using System.Text.Json; -using System.Text.Json.Serialization; +using System.Text.Json.Serialization; namespace ParadoxSaveParser.WebAPI; @@ -29,7 +27,7 @@ public class SaveFileMetadata private static readonly JsonSerializerOptions _jsonOptions = new() { WriteIndented = true }; public void SaveToFile() { - using var metaFile = File.Open(PathHelper.GetMetaFilePath(id), FileMode.CreateNew, FileAccess.Write); + using var metaFile = File.OpenWrite(PathHelper.GetMetaFilePath(id)); JsonSerializer.Serialize(metaFile, this, _jsonOptions); } } \ No newline at end of file diff --git a/ParadoxSaveParser.WebAPI/appsettings.Development.json b/ParadoxSaveParser.WebAPI/appsettings.Development.json deleted file mode 100644 index ff66ba6..0000000 --- a/ParadoxSaveParser.WebAPI/appsettings.Development.json +++ /dev/null @@ -1,8 +0,0 @@ -{ - "Logging": { - "LogLevel": { - "Default": "Information", - "Microsoft.AspNetCore": "Warning" - } - } -} diff --git a/ParadoxSaveParser.WebAPI/appsettings.json b/ParadoxSaveParser.WebAPI/appsettings.json deleted file mode 100644 index 4d56694..0000000 --- a/ParadoxSaveParser.WebAPI/appsettings.json +++ /dev/null @@ -1,9 +0,0 @@ -{ - "Logging": { - "LogLevel": { - "Default": "Information", - "Microsoft.AspNetCore": "Warning" - } - }, - "AllowedHosts": "*" -}