refactor!: rename the project from Monitor to MoShiro [CI]
ci/woodpecker/push/main-amd64-build Pipeline was successful

Namespaces, assemblies, solution, binaries, images, systemd units, config paths, environment variables, protocol methods and the Linux user are all renamed. Breaking: existing databases, node identities and installed units must be recreated.
This commit is contained in:
justme committed 2026-10-07 02:03:25 +08:00
1 parent 27ac7fb787
commit 2e146d4da7
309 files changed
+1565 -1572

No files matched your search

@@ -0,0 +1,95 @@
using System.Security.Cryptography;
using System.Text;
using Fleet.Core;
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.DependencyInjection;
using MoShiro.Backend.Application;
using MoShiro.Protocol;
namespace MoShiro.Backend.Api;
internal static class QueryHelpers
{
/// <summary>查询的统一超时(默认 10 s):超时返回 503,而不是让请求一直挂着。</summary>
public static async Task<IResult> GuardedAsync(HttpContext http, Func<CancellationToken, Task<IResult>> work)
{
BackendOptions options = http.RequestServices.GetRequiredService<BackendOptions>();
using var timeout = CancellationTokenSource.CreateLinkedTokenSource(http.RequestAborted);
timeout.CancelAfter(options.QueryTimeout);
try
{
return await work(timeout.Token);
}
catch (OperationCanceledException) when (!http.RequestAborted.IsCancellationRequested)
{
return Problems.Create(http, MoShiroErrors.StorageUnavailable, null);
}
}
/// <summary>
/// 缓存策略:已经结束的绝对区间(<c>to</c> 早于当前时间 − 2 分钟)可以永久缓存,并带 ETag;相对时间段不缓存。
/// 返回非空时表示命中 If-None-Match,直接返回 304。
/// </summary>
public static IResult? ApplyCaching(HttpContext http, QueryRange range, long nowMs, string material)
{
bool closed = !range.IsRelative && range.RequestedToMs < nowMs - (2 * Steps.Minute);
if (!closed)
{
http.Response.Headers.CacheControl = "no-store";
return null;
}
string etag = ETagOf(http.Request.Path + http.Request.QueryString.Value + material);
http.Response.Headers.CacheControl = "public, max-age=31536000, immutable";
http.Response.Headers.ETag = etag;
return MatchesIfNoneMatch(http, etag) ? TypedResults.StatusCode(StatusCodes.Status304NotModified) : null;
}
/// <summary>目录:<c>max-age=300</c>,并带 ETag。</summary>
public static IResult? ApplyCatalogCaching(HttpContext http, string material)
{
string etag = ETagOf(http.Request.Path + material);
http.Response.Headers.CacheControl = "public, max-age=300";
http.Response.Headers.ETag = etag;
return MatchesIfNoneMatch(http, etag) ? TypedResults.StatusCode(StatusCodes.Status304NotModified) : null;
}
public static string ETagOf(string material) =>
"\"" + Convert.ToHexStringLower(SHA256.HashData(Encoding.UTF8.GetBytes(material)))[..32] + "\"";
private static bool MatchesIfNoneMatch(HttpContext http, string etag)
{
foreach (string? candidate in http.Request.Headers.IfNoneMatch)
{
if (candidate is not null && candidate.Split(',', StringSplitOptions.TrimEntries).Contains(etag))
{
return true;
}
}
return false;
}
/// <summary>线路 id(<c>agent~probe</c>,或只有一个 Probe 时的 Agent id)→ 可以展示的线路。否则 404 / 422(不暴露被隐藏的线路存在与否)。</summary>
public static async ValueTask<Result<LineView>> FindVisibleLineAsync(HttpContext http, LineService lines, string id, CancellationToken ct)
{
Result<LineView> found = await lines.ResolveAsync(id, ct);
if (!found.TryGet(out LineView? line, out FleetError? error))
{
return Result<LineView>.Fail(error);
}
return line.Agent.Agent.Status == Fleet.Server.NodeStatus.Active && (line.Agent.Agent.PublicVisible || PublicGuard.CanSeeHidden(http)) && line.Probe.RevokedAtMs is null
? found
: Result<LineView>.Fail(MoShiroErrors.AgentNotFound, "line not found");
}
/// <summary>找到可以展示的线路:已激活,并且(对外可见,或者访问者已认证)。否则 404(不暴露被隐藏的线路存在与否)。</summary>
public static async ValueTask<Result<AgentView>> FindVisibleAgentAsync(HttpContext http, AgentService agents, string id, CancellationToken ct)
{
AgentView? view = await agents.GetAsync(new NodeId(id), ct);
return view is { Agent.Status: Fleet.Server.NodeStatus.Active } && (view.Agent.PublicVisible || PublicGuard.CanSeeHidden(http))
? Result<AgentView>.Ok(view)
: Result<AgentView>.Fail(MoShiroErrors.AgentNotFound, "agent not found");
}
}