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,65 @@
using Fleet.Server;
using Microsoft.AspNetCore.Builder;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using MoShiro.Backend.Api;
using MoShiro.Backend.Application;
using MoShiro.Backend.Gateway;
using MoShiro.Backend.Storage.Sqlite;
namespace MoShiro.Backend;
/// <summary>
/// 组合根:在这里显式注册所有层。配置来自 <c>appsettings.json</c>、环境变量(<c>MoShiro__DataDirectory</c> 等)和命令行。
/// 测试用 <see cref="Build"/> 在真实端口上启动整个后端。
/// </summary>
public static class BackendHost
{
public const string ConfigurationSection = "MoShiro";
public const int MaxRequestBodyBytes = 64 * 1024;
public static WebApplication Build(string[] args, Action<BackendOptions>? configureOptions = null, TimeProvider? time = null)
{
WebApplicationBuilder builder = WebApplication.CreateSlimBuilder(args);
var options = new BackendOptions();
builder.Configuration.GetSection(ConfigurationSection).Bind(options);
configureOptions?.Invoke(options);
builder.WebHost.ConfigureKestrel(kestrel => kestrel.Limits.MaxRequestBodySize = MaxRequestBodyBytes);
builder.Logging.AddSimpleConsole(console =>
{
console.SingleLine = true;
console.UseUtcTimestamp = true;
console.TimestampFormat = "yyyy-MM-ddTHH:mm:ss.fffZ ";
});
IServiceCollection services = builder.Services;
services.AddSingleton(time ?? TimeProvider.System);
services.AddMoShiroSqlite(options.DataDirectory);
// 国家和运营商:Backend 用节点的来源地址查一次(Probe、Agent 不访问任何外部服务);地址留空就不查
if (string.IsNullOrWhiteSpace(options.GeoLookupUrl))
{
services.AddSingleton<IGeoLookup, NoGeoLookup>();
}
else
{
services.AddSingleton<IGeoLookup>(new HttpGeoLookup(options.GeoLookupUrl));
}
services.AddMoShiroApplication(options);
services.AddMoShiroGateway();
services.AddMoShiroApi();
WebApplication app = builder.Build();
// 静默死亡的 Agent(断电、断网)不会关闭连接:15 秒一个 Ping,20 秒没有 Pong 就关闭,这样面板上的在线状态才可信
app.UseWebSockets(new WebSocketOptions { KeepAliveInterval = TimeSpan.FromSeconds(15), KeepAliveTimeout = TimeSpan.FromSeconds(20) });
app.UseMoShiroCors(options.CorsOrigins);
app.UseMoShiroApi();
app.MapMoShiroApi();
app.MapFleetEnrollment("/api/agent/v1/enroll");
app.MapFleetLink("/api/agent/v1/rpc");
return app;
}
}
@@ -0,0 +1,73 @@
using System.Text.Json;
using MoShiro.Backend.Application;
namespace MoShiro.Backend;
/// <summary>
/// 用 IP 归属查询服务(<c>GET {地址}/lookup?ip=…</c>)查国家和运营商。
/// 失败(超时、网络不通、服务返回错误)一律返回 null,不往外抛:展示信息没有也不影响别的功能。
/// </summary>
public sealed class HttpGeoLookup : IGeoLookup, IDisposable
{
private readonly HttpClient _http;
public HttpGeoLookup(string baseUrl)
{
_http = new HttpClient { BaseAddress = new Uri(baseUrl.TrimEnd('/') + "/"), Timeout = TimeSpan.FromSeconds(6) };
_http.DefaultRequestHeaders.UserAgent.ParseAdd("MoShiro");
}
public async ValueTask<GeoInfo?> LookupAsync(string host, CancellationToken ct)
{
try
{
using HttpResponseMessage response = await _http.GetAsync("lookup?ip=" + Uri.EscapeDataString(host), ct);
if (!response.IsSuccessStatusCode)
{
return null;
}
using JsonDocument doc = await JsonDocument.ParseAsync(await response.Content.ReadAsStreamAsync(ct), cancellationToken: ct);
return Parse(doc.RootElement);
}
catch (Exception ex) when (ex is HttpRequestException or TaskCanceledException or JsonException or InvalidOperationException)
{
return null;
}
}
/// <summary>只取国家代码、ASN、组织名;其余字段(城市、经纬度、网段……)一概不读。</summary>
public static GeoInfo? Parse(JsonElement root)
{
if (root.TryGetProperty("found", out JsonElement found) && found.ValueKind == JsonValueKind.False)
{
return null;
}
bool isPrivate = root.TryGetProperty("is_private", out JsonElement priv) && priv.ValueKind == JsonValueKind.True;
string? country = root.TryGetProperty("country_region", out JsonElement region) && region.TryGetProperty("iso_code", out JsonElement code) && code.ValueKind == JsonValueKind.String ? code.GetString() : null;
int? asn = null;
string? organization = null;
if (root.TryGetProperty("network", out JsonElement network))
{
if (network.TryGetProperty("asn", out JsonElement asnValue) && asnValue.TryGetInt32(out int number))
{
asn = number;
}
string? isp = network.TryGetProperty("isp", out JsonElement ispValue) && ispValue.ValueKind == JsonValueKind.String ? ispValue.GetString() : null;
string? org = network.TryGetProperty("organization", out JsonElement orgValue) && orgValue.ValueKind == JsonValueKind.String ? orgValue.GetString() : null;
organization = string.IsNullOrWhiteSpace(isp) ? org : isp;
}
return new GeoInfo(country, asn, organization, isPrivate);
}
public void Dispose() => _http.Dispose();
}
/// <summary>不查(<c>MoShiro__GeoLookupUrl</c> 留空)。</summary>
public sealed class NoGeoLookup : IGeoLookup
{
public ValueTask<GeoInfo?> LookupAsync(string host, CancellationToken ct) => ValueTask.FromResult<GeoInfo?>(null);
}
@@ -0,0 +1,38 @@
<Project>
<Import Project="Sdk.props" Sdk="Microsoft.NET.Sdk.Web" />
<Import Project="Sdk.props" Sdk="StuDev.AotAnywhere" Condition="'$(UseAotAnywhere)' == 'true'" />
<PropertyGroup>
<OutputType>Exe</OutputType>
<AssemblyName>moshiro-backend</AssemblyName>
<RootNamespace>MoShiro.Backend</RootNamespace>
<IsAotCompatible>true</IsAotCompatible>
<PublishAot>true</PublishAot>
<OptimizationPreference>Size</OptimizationPreference>
<StackTraceSupport>false</StackTraceSupport>
<EventSourceSupport>false</EventSourceSupport>
<DebuggerSupport>false</DebuggerSupport>
<MetadataUpdaterSupport>false</MetadataUpdaterSupport>
<HttpActivityPropagationSupport>false</HttpActivityPropagationSupport>
<IlcFoldIdenticalMethodBodies>true</IlcFoldIdenticalMethodBodies>
<StripSymbols>true</StripSymbols>
<!-- This service exposes APIs only; there are no static web assets to publish. -->
<StaticWebAssetsEnabled>false</StaticWebAssetsEnabled>
<SqliteStaticLink Condition="'$(SqliteStaticLink)' == '' and '$(PublishAot)' == 'true' and '$(RuntimeIdentifier)' != ''">true</SqliteStaticLink>
<EnableRequestDelegateGenerator>true</EnableRequestDelegateGenerator>
<EnableConfigurationBindingGenerator>true</EnableConfigurationBindingGenerator>
<OpenApiGenerateDocuments>true</OpenApiGenerateDocuments>
<OpenApiDocumentsDirectory>$(MSBuildProjectDirectory)/../../openapi</OpenApiDocumentsDirectory>
<OpenApiGenerateDocumentsOptions>--document-name v1 --file-name v1</OpenApiGenerateDocumentsOptions>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.Extensions.ApiDescription.Server" PrivateAssets="all" />
<PackageReference Include="SQLitePCLRaw.lib.e_sqlite3" Condition="'$(SqliteStaticLink)' != 'true'" />
<PackageReference Include="SQLitePCLRaw.lib.e_sqlite3" ExcludeAssets="native" Condition="'$(SqliteStaticLink)' == 'true'" />
<ProjectReference Include="..\MoShiro.Backend.Api\MoShiro.Backend.Api.csproj" />
<ProjectReference Include="..\MoShiro.Backend.Gateway\MoShiro.Backend.Gateway.csproj" />
<ProjectReference Include="..\MoShiro.Backend.Storage.Sqlite\MoShiro.Backend.Storage.Sqlite.csproj" />
</ItemGroup>
<Import Project="StaticSqlite.targets" />
<Import Project="Sdk.targets" Sdk="Microsoft.NET.Sdk.Web" />
<Import Project="Sdk.targets" Sdk="StuDev.AotAnywhere" Condition="'$(UseAotAnywhere)' == 'true'" />
</Project>
+3
View File
@@ -0,0 +1,3 @@
using MoShiro.Backend;
await BackendHost.Build(args).RunAsync();
@@ -0,0 +1,71 @@
<Project>
<PropertyGroup>
<_SqliteToolchainTargets>SetupOSSpecificProps</_SqliteToolchainTargets>
<_SqliteToolchainTargets Condition="'$(UseAotAnywhere)' == 'true'">SetupOSSpecificProps;OverwriteTargetTriple;SetPathToZig</_SqliteToolchainTargets>
</PropertyGroup>
<ItemGroup Condition="'$(SqliteStaticLink)' == 'true'">
<DirectPInvoke Include="e_sqlite3" />
</ItemGroup>
<Target Name="PrepareStaticSqliteSource" Condition="'$(SqliteStaticLink)' == 'true'">
<Error Condition="'$(RuntimeIdentifier)' != 'linux-x64' and '$(RuntimeIdentifier)' != 'linux-arm64' and
'$(RuntimeIdentifier)' != 'linux-musl-x64' and '$(RuntimeIdentifier)' != 'linux-musl-arm64' and
'$(RuntimeIdentifier)' != 'osx-x64' and '$(RuntimeIdentifier)' != 'osx-arm64' and
'$(RuntimeIdentifier)' != 'win-x64' and '$(RuntimeIdentifier)' != 'win-arm64'"
Text="Static SQLite is configured for the eight supported x64/arm64 Linux, musl, macOS and Windows targets. Unsupported RID: $(RuntimeIdentifier)." />
<PropertyGroup>
<_SqliteCache>$([System.IO.Path]::GetFullPath('$(BaseIntermediateOutputPath)sqlite/$(MoShiroSqliteSourceVersion)/'))</_SqliteCache>
<_SqliteArchive>$(_SqliteCache)sqlite-amalgamation-$(MoShiroSqliteSourceVersion).zip</_SqliteArchive>
<_SqliteSource>$(_SqliteCache)sqlite-amalgamation-$(MoShiroSqliteSourceVersion)/sqlite3.c</_SqliteSource>
<_SqliteObjectDirectory>$(_SqliteCache)$(RuntimeIdentifier)/$(UseAotAnywhere)/</_SqliteObjectDirectory>
<_SqliteObject>$(_SqliteObjectDirectory)e_sqlite3.o</_SqliteObject>
<_SqliteObject Condition="$(RuntimeIdentifier.StartsWith('win'))">$(_SqliteObjectDirectory)e_sqlite3.obj</_SqliteObject>
</PropertyGroup>
<MakeDir Directories="$(_SqliteObjectDirectory)" />
<DownloadFile SourceUrl="https://www.sqlite.org/$(MoShiroSqliteSourceYear)/sqlite-amalgamation-$(MoShiroSqliteSourceVersion).zip"
DestinationFolder="$(_SqliteCache)" Condition="!Exists('$(_SqliteArchive)')" />
<GetFileHash Files="$(_SqliteArchive)" Algorithm="SHA256">
<Output TaskParameter="Hash" PropertyName="_SqliteArchiveHash" />
</GetFileHash>
<Error Condition="'$(_SqliteArchiveHash.ToLowerInvariant())' != '$(MoShiroSqliteSourceSha256)'"
Text="SQLite source SHA256 mismatch: $(_SqliteArchiveHash)." />
<Unzip SourceFiles="$(_SqliteArchive)" DestinationFolder="$(_SqliteCache)" Condition="!Exists('$(_SqliteSource)')" />
</Target>
<Target Name="CompileStaticSqliteObject" Condition="'$(SqliteStaticLink)' == 'true'"
DependsOnTargets="PrepareStaticSqliteSource;$(_SqliteToolchainTargets)"
Inputs="$(_SqliteSource);$(MSBuildThisFileFullPath)" Outputs="$(_SqliteObject)">
<PropertyGroup>
<_SqliteUseMsvc>false</_SqliteUseMsvc>
<_SqliteUseMsvc Condition="$(RuntimeIdentifier.StartsWith('win')) and '$(_AotAnywhereWindowsCross)' != 'true'">true</_SqliteUseMsvc>
<_SqliteCompiler>&quot;$(CppCompilerAndLinker)&quot;</_SqliteCompiler>
<_SqliteCompiler Condition="'$(CppCompilerAndLinker)' == ''">cc</_SqliteCompiler>
<_SqliteCompiler Condition="'$(TargetTriple)' != ''">$(_SqliteCompiler) --target=$(TargetTriple)</_SqliteCompiler>
<_SqliteCompiler Condition="'$(SysRoot)' != ''">$(_SqliteCompiler) --sysroot=&quot;$(SysRoot)&quot;</_SqliteCompiler>
<_SqliteCompiler Condition="'$(UseAotAnywhere)' == 'true'">&quot;$(_AotAnywhereZigExe)&quot; cc -target $(TargetTriple)</_SqliteCompiler>
<_SqliteCompiler Condition="'$(_SqliteUseMsvc)' == 'true'">cl</_SqliteCompiler>
</PropertyGroup>
<Message Importance="high" Text="Compiling SQLite $(MoShiroSqliteSourceVersion) into the $(RuntimeIdentifier) NativeAOT executable." />
<Exec Condition="'$(_SqliteUseMsvc)' != 'true'"
Command="$(_SqliteCompiler) -Os -fPIC -c &quot;$(_SqliteSource)&quot; -o &quot;$(_SqliteObject)&quot; -DSQLITE_API= -DSQLITE_THREADSAFE=1 -DSQLITE_DEFAULT_MEMSTATUS=0 -DSQLITE_ENABLE_COLUMN_METADATA -DSQLITE_ENABLE_FTS4 -DSQLITE_ENABLE_FTS5 -DSQLITE_ENABLE_MATH_FUNCTIONS -DSQLITE_ENABLE_RTREE -DSQLITE_ENABLE_SNAPSHOT -DSQLITE_ENABLE_UNLOCK_NOTIFY" />
<Exec Condition="'$(_SqliteUseMsvc)' == 'true'"
Command="cl /nologo /O1 /MT /c &quot;$(_SqliteSource)&quot; /Fo&quot;$(_SqliteObject)&quot; /DSQLITE_API= /DSQLITE_THREADSAFE=1 /DSQLITE_DEFAULT_MEMSTATUS=0 /DSQLITE_ENABLE_COLUMN_METADATA /DSQLITE_ENABLE_FTS4 /DSQLITE_ENABLE_FTS5 /DSQLITE_ENABLE_MATH_FUNCTIONS /DSQLITE_ENABLE_RTREE /DSQLITE_ENABLE_SNAPSHOT /DSQLITE_ENABLE_UNLOCK_NOTIFY" />
</Target>
<!-- AotAnywhere runs before LinkNative; register the object before either linker starts. -->
<Target Name="LinkStaticSqlite" Condition="'$(SqliteStaticLink)' == 'true'"
BeforeTargets="AotAnywhereDirectLinkNative;AotAnywhereDirectLinkMacNative;AotAnywhereDirectLinkWindows;LinkNative"
DependsOnTargets="CompileStaticSqliteObject">
<ItemGroup>
<NativeLibrary Include="$(_SqliteObject)" />
<LinkerArg Include="&quot;$(_SqliteObject)&quot;" />
</ItemGroup>
</Target>
<Target Name="RemoveStaticSqliteRuntimeFiles" Condition="'$(SqliteStaticLink)' == 'true'" AfterTargets="ComputeFilesToPublish">
<ItemGroup>
<ResolvedFileToPublish Remove="@(ResolvedFileToPublish)"
Condition="'%(ResolvedFileToPublish.Filename)' == 'libe_sqlite3' or '%(ResolvedFileToPublish.Filename)' == 'e_sqlite3'" />
</ItemGroup>
</Target>
</Project>