Skip to content

Commit 9c98870

Browse files
authored
feat: add embedded ASP.NET LocalUpdateServer for simulate module (#33)
- Add FrameworkReference Microsoft.AspNetCore.App - LocalUpdateServer: minimal API with three endpoints - GET /Upgrade/Verification - returns version info with local patch URL - POST /Upgrade/Report - accepts update status reports - GET /patch/{filename} - serves patch .zip files - Auto-detect bound port, static file lookup for patches
1 parent 42985bb commit 9c98870

2 files changed

Lines changed: 108 additions & 0 deletions

File tree

src/GeneralUpdate.Tools.csproj

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,4 +31,8 @@
3131
<PackageReference Include="GeneralUpdate.Core" Version="10.4.6" />
3232
<PackageReference Include="Newtonsoft.Json" Version="13.0.3" />
3333
</ItemGroup>
34+
35+
<ItemGroup>
36+
<FrameworkReference Include="Microsoft.AspNetCore.App" />
37+
</ItemGroup>
3438
</Project>

src/Services/LocalUpdateServer.cs

Lines changed: 104 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,104 @@
1+
using System;
2+
using System.Collections.Generic;
3+
using System.IO;
4+
using System.Linq;
5+
using System.Threading.Tasks;
6+
using Microsoft.AspNetCore.Builder;
7+
using Microsoft.AspNetCore.Hosting;
8+
using Microsoft.AspNetCore.Http;
9+
using Microsoft.Extensions.DependencyInjection;
10+
using Microsoft.Extensions.Hosting;
11+
12+
namespace GeneralUpdate.Tools.Services;
13+
14+
public class LocalUpdateServer : IAsyncDisposable
15+
{
16+
private WebApplication? _app;
17+
private int _port;
18+
private Task? _runTask;
19+
20+
public int Port => _port;
21+
public string BaseUrl => $"http://127.0.0.1:{_port}";
22+
23+
public List<(string CurrentVersion, string TargetVersion, string Hash, string ZipPath, int AppType)> Updates { get; } = new();
24+
25+
public async Task StartAsync(int port = 5000)
26+
{
27+
var builder = WebApplication.CreateBuilder();
28+
builder.WebHost.UseUrls($"http://127.0.0.1:{port}");
29+
30+
_app = builder.Build();
31+
32+
// GET /Upgrade/Verification
33+
_app.MapGet("/Upgrade/Verification", async (HttpContext context) =>
34+
{
35+
var q = context.Request.Query;
36+
var currentVer = q["currentVersion"].ToString();
37+
_ = int.TryParse(q["appType"].ToString(), out var appType);
38+
39+
var match = Updates.Find(u => u.CurrentVersion == currentVer);
40+
if (match == default)
41+
{
42+
await context.Response.WriteAsJsonAsync(new { code = 204, body = Array.Empty<object>() });
43+
return;
44+
}
45+
46+
var body = new[]
47+
{
48+
new
49+
{
50+
name = Path.GetFileName(match.ZipPath),
51+
version = match.TargetVersion,
52+
hash = match.Hash,
53+
url = $"{BaseUrl}/patch/{Uri.EscapeDataString(Path.GetFileName(match.ZipPath))}",
54+
appType = match.AppType,
55+
releaseDate = DateTime.UtcNow.ToString("yyyy-MM-ddTHH:mm:ssZ"),
56+
isForcibly = false
57+
}
58+
};
59+
await context.Response.WriteAsJsonAsync(new { code = 200, body });
60+
});
61+
62+
// POST /Upgrade/Report
63+
_app.MapPost("/Upgrade/Report", () => Results.Ok(new { code = 200 }));
64+
65+
// GET /patch/{filename}
66+
_app.MapGet("/patch/{filename}", async (string filename) =>
67+
{
68+
var filePath = LocalUpdateServerFiles.TryGet(filename);
69+
if (filePath == null || !File.Exists(filePath))
70+
return Results.NotFound();
71+
return Results.File(filePath, "application/zip", filename);
72+
});
73+
74+
_runTask = _app.RunAsync();
75+
// Give Kestrel a moment to bind
76+
await Task.Delay(500);
77+
// Read actual port from addresses
78+
var urls = _app.Urls;
79+
if (urls.Count > 0)
80+
{
81+
var uri = new Uri(urls.First());
82+
_port = uri.Port;
83+
}
84+
}
85+
86+
public async ValueTask DisposeAsync()
87+
{
88+
if (_app != null)
89+
{
90+
await _app.StopAsync();
91+
await _app.DisposeAsync();
92+
}
93+
if (_runTask != null)
94+
await _runTask;
95+
}
96+
}
97+
98+
internal static class LocalUpdateServerFiles
99+
{
100+
private static readonly Dictionary<string, string> _files = new();
101+
public static void Register(string filename, string filePath) => _files[filename] = filePath;
102+
public static string? TryGet(string filename) => _files.TryGetValue(filename, out var p) ? p : null;
103+
public static void Clear() => _files.Clear();
104+
}

0 commit comments

Comments
 (0)