chore: initial project import

This commit is contained in:
justme committed 2026-10-04 21:46:39 +08:00
commit 3fc4e5ca53
347 files changed
+56172

No files matched your search

@@ -0,0 +1,9 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<IsAotCompatible>true</IsAotCompatible>
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\Fleet.Core\Fleet.Core.csproj" />
<ProjectReference Include="..\Fleet.Rpc\Fleet.Rpc.csproj" />
</ItemGroup>
</Project>
@@ -0,0 +1,15 @@
namespace Fleet.Server;
public sealed class FleetServerOptions
{
/// <summary>节点 ID 的类型前缀,Monitor 使用 <c>agt</c>。</summary>
public string NodeIdPrefix { get; set; } = "node";
public int MaxFrameBytes { get; set; } = 16 << 20;
public TimeSpan ChallengeTtl { get; set; } = TimeSpan.FromSeconds(30);
public TimeSpan HeartbeatTimeout { get; set; } = TimeSpan.FromSeconds(45);
public TimeSpan RequestTimeout { get; set; } = TimeSpan.FromSeconds(15);
}
@@ -0,0 +1,21 @@
using Fleet.Core;
namespace Fleet.Server;
/// <summary>Fleet 服务端需要的存储。产品可以自己实现(Monitor 把它放进 monitor.db)。</summary>
public interface IFleetStore
{
ValueTask<JoinTokenRecord?> FindJoinTokenAsync(ReadOnlyMemory<byte> tokenSha256, CancellationToken ct);
/// <summary>原子地把令牌使用次数加一。令牌已吊销、过期或用完时返回 false。</summary>
ValueTask<bool> TryConsumeJoinTokenAsync(string tokenId, long nowMs, CancellationToken ct);
ValueTask<Result<NodeRecord>> CreateNodeAsync(NewNode node, CancellationToken ct);
ValueTask<NodeRecord?> GetNodeAsync(NodeId id, CancellationToken ct);
/// <summary>按名称查找状态不是 pending 的节点(用于判断注册时的名称冲突)。</summary>
ValueTask<NodeRecord?> FindEstablishedNodeByNameAsync(string name, CancellationToken ct);
ValueTask UpdatePresenceAsync(NodeId id, Presence presence, CancellationToken ct);
}
@@ -0,0 +1,48 @@
using Fleet.Core;
namespace Fleet.Server;
/// <summary>pending 能连接,但不下发状态、不接收上报;disabled 拒绝连接。</summary>
public enum NodeStatus
{
Pending,
Active,
Disabled,
}
public sealed record NodeRecord(
NodeId Id,
string Name,
NodeStatus Status,
byte[] PublicKey,
byte[] TlsSpki,
IReadOnlyList<string> Labels,
string? MetaJson,
string? NodeVersion,
long CreatedAtMs,
long? LastSeenAtMs);
public sealed record NewNode(
NodeId Id,
string Name,
NodeStatus Status,
byte[] PublicKey,
byte[] TlsSpki,
IReadOnlyList<string> Labels,
string? MetaJson,
string? NodeVersion,
long NowMs);
public sealed record JoinTokenRecord(
string Id,
byte[] TokenSha256,
string? Note,
IReadOnlyList<string> Labels,
bool AutoApprove,
int MaxUses,
int Uses,
long ExpiresAtMs,
long? RevokedAtMs,
long CreatedAtMs);
public sealed record Presence(bool Online, long AtMs, string? NodeVersion, IReadOnlyList<string> Capabilities, string? HelloMetaJson);
@@ -0,0 +1,65 @@
using System.Text.Json;
using Fleet.Core;
using Fleet.Rpc;
namespace Fleet.Server;
public sealed record EnrollmentContext(string? RemoteIp, JsonElement? ProductMeta);
/// <summary>产品挂钩:节点注册成功之后调用。</summary>
public interface IEnrollmentHook
{
ValueTask OnEnrolledAsync(NodeRecord node, EnrollmentContext context, CancellationToken ct);
}
public enum NodeEventKind
{
Online,
Offline,
Hello,
Heartbeat,
}
public sealed record NodeEvent(NodeEventKind Kind, NodeRecord Node, long AtMs, HelloParams? Hello = null, JsonElement? Extra = null);
public interface INodeEventSink
{
ValueTask OnEventAsync(NodeEvent nodeEvent, CancellationToken ct);
}
/// <summary>期望状态提供者:为每个节点计算期望状态的完整快照(天然幂等)。</summary>
public interface IStateProvider
{
/// <summary>状态文档类型,例如 <c>task_set</c>。</summary>
string Type { get; }
ValueTask<StateDocument?> GetAsync(NodeRecord node, CancellationToken ct);
/// <summary>节点应用之后回调,<c>rejected[]</c> 带结构化原因。</summary>
ValueTask OnAppliedAsync(NodeRecord node, long version, StateSyncResult result, CancellationToken ct);
}
public sealed record StateDocument(long Version, JsonElement Doc);
public enum InboxOutcome
{
Stored,
Duplicate,
Rejected,
}
/// <summary>收件箱:持久化之后才返回 Stored,框架随后才回复 ACK。</summary>
public interface IInbox
{
byte Channel { get; }
ValueTask<InboxOutcome> HandleAsync(NodeRecord node, Guid msgId, ReadOnlyMemory<byte> payload, CancellationToken ct);
}
/// <summary>通知 Fleet:某个节点的期望状态变了,需要重新计算并推送。</summary>
public interface IStateNotifier
{
void NotifyChanged(NodeId node);
void NotifyAllChanged();
}