refactor!: rename the project from Monitor to MoShiro [CI]
ci/woodpecker/push/main-amd64-build Pipeline was successful
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:
1 parent
27ac7fb787
commit
2e146d4da7
309 files changed
+1565
-1572
No files matched your search
@@ -0,0 +1,95 @@
|
||||
using System.Diagnostics;
|
||||
using Fleet.Core;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using MoShiro.Backend.Application;
|
||||
using MoShiro.Protocol;
|
||||
|
||||
namespace MoShiro.Backend.Api;
|
||||
|
||||
/// <summary>错误码到 HTTP 状态的映射:只在 Api 层做一次。</summary>
|
||||
public static class ErrorCodeHttpMap
|
||||
{
|
||||
// 必须声明在 s_map 之前:静态字段按文本顺序初始化,BuildMap 会用到它
|
||||
public static readonly ErrorCode NotImplemented = new("not_implemented");
|
||||
|
||||
private static readonly Dictionary<string, int> s_map = BuildMap();
|
||||
|
||||
public static int StatusOf(ErrorCode code) => s_map.GetValueOrDefault(code.Value, StatusCodes.Status500InternalServerError);
|
||||
|
||||
public static string TitleOf(int status) => status switch
|
||||
{
|
||||
400 => "Validation failed",
|
||||
401 => "Unauthorized",
|
||||
403 => "Forbidden",
|
||||
404 => "Not found",
|
||||
409 => "Conflict",
|
||||
410 => "Gone",
|
||||
412 => "Precondition failed",
|
||||
413 => "Payload too large",
|
||||
422 => "Unprocessable request",
|
||||
429 => "Too many requests",
|
||||
501 => "Not implemented",
|
||||
503 => "Service unavailable",
|
||||
_ => "Internal error",
|
||||
};
|
||||
|
||||
private static Dictionary<string, int> BuildMap()
|
||||
{
|
||||
var map = new Dictionary<string, int>(StringComparer.Ordinal);
|
||||
void Add(int status, params ErrorCode[] codes)
|
||||
{
|
||||
foreach (ErrorCode code in codes)
|
||||
{
|
||||
map[code.Value] = status;
|
||||
}
|
||||
}
|
||||
|
||||
Add(400, MoShiroErrors.ValidationFailed, MoShiroErrors.BudgetExceeded, FleetErrors.InvalidParams);
|
||||
Add(401, MoShiroErrors.Unauthorized, MoShiroErrors.CsrfFailed, FleetErrors.Unauthorized);
|
||||
Add(403, MoShiroErrors.Forbidden, MoShiroErrors.SitePrivate, FleetErrors.Forbidden, FleetErrors.ProofInvalid);
|
||||
Add(
|
||||
404, MoShiroErrors.NotFound, MoShiroErrors.AgentNotFound, MoShiroErrors.ProbeNotFound, MoShiroErrors.TaskNotFound, MoShiroErrors.TestNotFound, MoShiroErrors.ChunkNotFound,
|
||||
MoShiroErrors.TokenNotFound, MoShiroErrors.UnknownKind, MoShiroErrors.UnknownPlan, FleetErrors.NotFound);
|
||||
Add(409, MoShiroErrors.NameConflict, MoShiroErrors.ProbeRevoked, MoShiroErrors.ProbeOffline, MoShiroErrors.TestBusy, MoShiroErrors.AgentNotActive, MoShiroErrors.AgentOffline, FleetErrors.NameConflict, FleetErrors.NodeOffline);
|
||||
Add(410, MoShiroErrors.RawExpired, MoShiroErrors.JoinTokenExpired, FleetErrors.JoinTokenExpired);
|
||||
Add(412, MoShiroErrors.PreconditionFailed);
|
||||
Add(413, MoShiroErrors.PayloadTooLarge);
|
||||
Add(422, MoShiroErrors.ProbeRequired, MoShiroErrors.RangeTooLarge, MoShiroErrors.InvalidStep, MoShiroErrors.InvalidRange, MoShiroErrors.UnknownMetric, MoShiroErrors.UnknownPreset, MoShiroErrors.TooManyMetrics, FleetErrors.Unsupported);
|
||||
Add(429, MoShiroErrors.TooManyRequests, MoShiroErrors.TooManyStreams);
|
||||
Add(503, MoShiroErrors.StorageUnavailable, FleetErrors.Timeout);
|
||||
map[NotImplemented.Value] = 501;
|
||||
return map;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/// <summary>所有 4xx/5xx 响应都使用 problem+json;5xx 不泄露异常信息,只提供 trace_id。</summary>
|
||||
public static class Problems
|
||||
{
|
||||
public const string ContentType = "application/problem+json";
|
||||
|
||||
public static IResult From(HttpContext http, FleetError error)
|
||||
{
|
||||
int status = ErrorCodeHttpMap.StatusOf(error.Code);
|
||||
IReadOnlyDictionary<string, string[]>? fields = error is ValidationError v
|
||||
? v.Fields.ToDictionary(f => f.Key, f => new[] { f.Value })
|
||||
: null;
|
||||
return Create(http, status, error.Code.Value, status >= 500 ? null : error.Message, fields);
|
||||
}
|
||||
|
||||
public static IResult Create(HttpContext http, ErrorCode code, string? detail) =>
|
||||
Create(http, ErrorCodeHttpMap.StatusOf(code), code.Value, detail, null);
|
||||
|
||||
public static IResult Create(HttpContext http, int status, string code, string? detail, IReadOnlyDictionary<string, string[]>? errors = null)
|
||||
{
|
||||
string? trace = Activity.Current?.Id ?? http.TraceIdentifier;
|
||||
var problem = new ProblemDto("about:blank", ErrorCodeHttpMap.TitleOf(status), status, code, detail, trace, errors);
|
||||
return TypedResults.Json(problem, ApiJsonContext.Default.ProblemDto, ContentType, status);
|
||||
}
|
||||
|
||||
public static IResult RetryAfter(HttpContext http, FleetError error, int seconds)
|
||||
{
|
||||
http.Response.Headers.RetryAfter = seconds.ToString(System.Globalization.CultureInfo.InvariantCulture);
|
||||
return From(http, error);
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user