Files
MeWeb/src/MeWeb.Server/ServerApi.cs
T
thepraandClaude Opus 5.5 0dffbc27d6 The checks engine, the path and the cards: me-web? 0.1 reads the connection
MeWeb.Core runs seven checks concurrently and reports each as it settles:
online or behind a login page, this connection, the public IPv4 and IPv6
seen from v4./v6.meweb, the provider from a built-in map, the router
(UPnP IGD, NAT-PMP and PCP, read-only), address sharing (router WAN
against the echo, TTL hops only as a hint), and the history kept on the
device. The app draws the path from this device to the internet and lights
it node by node, with a card per check and a sheet listing every host it
contacts.

The provider map is iptoasn's table compressed to 2.1 MB by
tools/knowledge.cs and built before every app release; eleven Italian
provider profiles carry each fact with who said it, the source and the
month. The server gains a download page (en, it) and /download/{platform}
from the latest Gitea release.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012CzABvBkbcFqoHdmi8b9WB
2026-10-15 04:39:03 +02:00

47 lines
1.9 KiB
C#

using System.Net.Sockets;
using MeWeb.Shared;
namespace MeWeb.Server;
public static class ServerApi
{
public static WebApplication MapMeWebApi(this WebApplication app)
{
app.MapGet(Endpoints.Server.GET_Info, Info);
app.MapGet(Endpoints.Server.GET_Ip, Ip);
app.MapGet(Endpoints.Downloads.GET_Page, Page);
app.MapGet(Endpoints.Downloads.GET_Platform, Download);
return app;
}
static ViewInfo Info() => new() { Version = BuildInfo.Version, Commit = BuildInfo.Commit, BuiltAt = BuildInfo.BuiltAt };
static IResult Ip(HttpContext context)
{
var address = context.Connection.RemoteIpAddress;
if (address is null) return Results.BadRequest();
if (address.IsIPv4MappedToIPv6) address = address.MapToIPv4();
var family = address.AddressFamily == AddressFamily.InterNetworkV6 ? "ipv6" : "ipv4";
return Results.Ok(new ViewIp { Address = address.ToString(), Family = family });
}
static async Task<IResult> Page(HttpContext context, ReleaseFeed feed, CancellationToken ct)
{
var asked = context.Request.Query["lang"].ToString();
var language = asked is "it" or "en" ? asked : context.Request.Headers.AcceptLanguage.ToString().StartsWith("it", StringComparison.OrdinalIgnoreCase) ? "it" : "en";
var release = await feed.Latest(ct);
context.Response.Headers.CacheControl = "no-cache";
context.Response.Headers.ContentLanguage = language;
context.Response.Headers.Vary = "Accept-Language";
return Results.Content(DownloadPage.Render(release, language), "text/html; charset=utf-8");
}
static async Task<IResult> Download(string platform, ReleaseFeed feed, CancellationToken ct)
{
if (!ReleaseFeed.Platforms.Any(p => p.Platform == platform)) return Results.NotFound();
var asset = ReleaseFeed.Asset(await feed.Latest(ct), platform);
return asset?.Url is null ? Results.Redirect(Endpoints.Gitea.Releases) : Results.Redirect(asset.Url);
}
}