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