Squash of recent dev work: return_session login, backend switcher, line detail timeline, overview strips, node platform/country/ISP info with flags and icons, build info (version, tag, time) behind site.show_build, and the acrylic display theme.
216 lines
9.6 KiB
C#
216 lines
9.6 KiB
C#
using Fleet.Core;
|
|
using Fleet.Server;
|
|
using Monitor.Protocol;
|
|
|
|
namespace Monitor.Backend.Application;
|
|
|
|
public interface IAgentStore
|
|
{
|
|
ValueTask<IReadOnlyList<AgentRecord>> ListAsync(CancellationToken ct);
|
|
|
|
ValueTask<AgentRecord?> GetAsync(NodeId id, CancellationToken ct);
|
|
|
|
/// <summary>注册之后补充公开地址与端口(仅在记录还是注册时的默认值时才写入)。</summary>
|
|
ValueTask ApplyEnrollmentAsync(NodeId id, AgentEnrollment enrollment, CancellationToken ct);
|
|
|
|
/// <summary>应用 merge-patch。<paramref name="expectedRowVersion"/> 不为 null 且不匹配时返回 precondition_failed。</summary>
|
|
ValueTask<Result<AgentRecord>> PatchAsync(NodeId id, AgentPatch patch, long? expectedRowVersion, CancellationToken ct);
|
|
|
|
/// <summary>pending → active、active ↔ disabled。激活时名称不能与其他已建立的节点冲突。</summary>
|
|
ValueTask<Result<AgentRecord>> SetStatusAsync(NodeId id, NodeStatus status, CancellationToken ct);
|
|
|
|
/// <summary>接管:把同名旧节点的公钥换成待审批节点的公钥,保留历史数据,并删除待审批记录。</summary>
|
|
ValueTask<Result<AgentRecord>> TakeoverAsync(NodeId pendingId, CancellationToken ct);
|
|
|
|
ValueTask DeleteAsync(NodeId id, CancellationToken ct);
|
|
|
|
ValueTask ApplyHelloAsync(NodeId id, AgentHello hello, CancellationToken ct);
|
|
|
|
ValueTask SetTaskVersionAsync(NodeId id, long version, string taskHash, CancellationToken ct);
|
|
|
|
ValueTask<string?> GetTaskHashAsync(NodeId id, CancellationToken ct);
|
|
|
|
ValueTask SetAppliedTaskStateAsync(NodeId id, long appliedVersion, string? rejectedJson, CancellationToken ct);
|
|
}
|
|
|
|
/// <summary>节点的展示信息(系统、架构、国家、运营商)。</summary>
|
|
public interface INodeMetaStore
|
|
{
|
|
ValueTask<IReadOnlyList<NodeMeta>> ListAsync(CancellationToken ct);
|
|
|
|
ValueTask SaveAsync(NodeMeta meta, CancellationToken ct);
|
|
|
|
ValueTask DeleteAsync(string nodeId, CancellationToken ct);
|
|
}
|
|
|
|
/// <summary>查一个 IP(或域名)的归属:国家、ASN、组织。实现负责超时和失败(失败返回 null,不抛)。</summary>
|
|
public interface IGeoLookup
|
|
{
|
|
ValueTask<GeoInfo?> LookupAsync(string host, CancellationToken ct);
|
|
}
|
|
|
|
public interface IJoinTokenStore
|
|
{
|
|
ValueTask<JoinTokenInfo> CreateAsync(string id, byte[] tokenSha256, string? note, IReadOnlyList<string> labels, bool autoApprove, int maxUses, long expiresAtMs, long nowMs, CancellationToken ct);
|
|
|
|
ValueTask<IReadOnlyList<JoinTokenInfo>> ListAsync(CancellationToken ct);
|
|
|
|
ValueTask<bool> RevokeAsync(string id, long nowMs, CancellationToken ct);
|
|
|
|
ValueTask<bool> DeleteAsync(string id, CancellationToken ct);
|
|
}
|
|
|
|
public interface IProbeStore
|
|
{
|
|
ValueTask<IReadOnlyList<ProbeRecord>> ListAsync(CancellationToken ct);
|
|
|
|
ValueTask<ProbeRecord?> GetAsync(string id, CancellationToken ct);
|
|
|
|
/// <summary>新增或整条更新(轮换密钥、改名都走这里)。</summary>
|
|
ValueTask SaveAsync(ProbeRecord probe, CancellationToken ct);
|
|
|
|
/// <summary>吊销:保留记录,公钥立即从所有 Agent 的名单里移除。已经吊销的返回 false。</summary>
|
|
ValueTask<bool> RevokeAsync(string id, long nowMs, CancellationToken ct);
|
|
|
|
ValueTask<bool> DeleteAsync(string id, CancellationToken ct);
|
|
}
|
|
|
|
public interface ISettingsStore
|
|
{
|
|
ValueTask<string?> GetAsync(string key, CancellationToken ct);
|
|
|
|
ValueTask SetAsync(string key, string value, CancellationToken ct);
|
|
|
|
ValueTask<IReadOnlyDictionary<string, string>> GetAllAsync(CancellationToken ct);
|
|
|
|
/// <summary>原子地加一并返回新值(用于版本号计数器)。</summary>
|
|
ValueTask<long> IncrementAsync(string key, CancellationToken ct);
|
|
}
|
|
|
|
public interface ITaskStore
|
|
{
|
|
ValueTask<IReadOnlyList<TaskRecord>> ListAsync(CancellationToken ct);
|
|
|
|
ValueTask<TaskRecord?> GetAsync(TaskId id, CancellationToken ct);
|
|
|
|
ValueTask CreateAsync(TaskRecord task, CancellationToken ct);
|
|
|
|
ValueTask<Result<TaskRecord>> UpdateAsync(TaskRecord task, long? expectedRowVersion, CancellationToken ct);
|
|
|
|
ValueTask DeleteAsync(TaskId id, CancellationToken ct);
|
|
}
|
|
|
|
public interface ITestStore
|
|
{
|
|
ValueTask CreateAsync(TestRecord test, CancellationToken ct);
|
|
|
|
ValueTask<TestRecord?> GetAsync(TestId id, CancellationToken ct);
|
|
|
|
ValueTask<IReadOnlyList<TestRecord>> ListAsync(NodeId? agentId, ProbeId? probeId, int limit, CancellationToken ct);
|
|
|
|
ValueTask<bool> UpdateStatusAsync(TestId id, string status, int? phase, string? errorCode, long nowMs, CancellationToken ct);
|
|
|
|
ValueTask SetPhaseCountAsync(TestId id, int phaseCount, CancellationToken ct);
|
|
|
|
ValueTask<bool> HasActiveAsync(NodeId agentId, ProbeId probeId, CancellationToken ct);
|
|
|
|
ValueTask SaveResultAsync(TestResultRecord result, CancellationToken ct);
|
|
|
|
ValueTask<TestResultRecord?> GetResultAsync(TestId id, CancellationToken ct);
|
|
|
|
/// <summary>已成功结束但还没有结果的测试(用于补算结果)。</summary>
|
|
ValueTask<IReadOnlyList<TestRecord>> ListNeedingResultAsync(int limit, CancellationToken ct);
|
|
|
|
/// <summary>把长时间处于 queued/running 的测试标记为 failed(例如 Backend 重启或节点失联)。</summary>
|
|
ValueTask<int> FailStaleAsync(long olderThanMs, string errorCode, long nowMs, CancellationToken ct);
|
|
}
|
|
|
|
public interface IEventStore
|
|
{
|
|
ValueTask AddAsync(NodeId? agentId, string kind, long atMs, string? detail, CancellationToken ct);
|
|
|
|
ValueTask<IReadOnlyList<EventRecord>> ListAsync(NodeId? agentId, string? kind, long fromMs, long toMs, long? beforeId, int limit, CancellationToken ct);
|
|
|
|
ValueTask<int> DeleteOlderThanAsync(long cutoffMs, CancellationToken ct);
|
|
}
|
|
|
|
/// <summary>主机资源占用的历史:每个 Agent 每分钟一个样本。</summary>
|
|
public interface IHostStore
|
|
{
|
|
ValueTask SaveAsync(NodeId agent, long minuteMs, Monitor.Protocol.HostSnapshot snapshot, CancellationToken ct);
|
|
|
|
ValueTask<IReadOnlyList<HostSample>> ReadAsync(NodeId agent, long fromMs, long toMs, CancellationToken ct);
|
|
|
|
ValueTask<int> DeleteOlderThanAsync(long cutoffMs, CancellationToken ct);
|
|
}
|
|
|
|
public sealed record HostSample(long AtMs, double? Cpu, long? MemUsed, long? MemTotal, long? DiskUsed, long? DiskTotal, long? NetRxBps, long? NetTxBps, long? UptimeSeconds);
|
|
|
|
public interface IRawStore
|
|
{
|
|
/// <summary>追加写入段文件 → 刷盘 → 写索引;<c>chunk_id</c> 已存在时返回 false(调用方仍然确认)。</summary>
|
|
ValueTask<bool> AppendAsync(RawChunkAppend chunk, CancellationToken ct);
|
|
|
|
ValueTask<RawChunkMeta?> GetMetaAsync(ChunkId id, CancellationToken ct);
|
|
|
|
ValueTask<IReadOnlyList<RawChunkMeta>> FindAsync(RawQuery query, CancellationToken ct);
|
|
|
|
ValueTask<byte[]?> ReadAsync(RawChunkMeta meta, CancellationToken ct);
|
|
|
|
/// <summary>删除早于截止日期的段文件和索引。持续任务与按需测试保留期不同。</summary>
|
|
ValueTask<int> DeleteExpiredAsync(DateOnly continuousBefore, DateOnly testsBefore, CancellationToken ct);
|
|
|
|
/// <summary>时间范围内有原始块的序列(用于重算)。</summary>
|
|
ValueTask<IReadOnlyList<(NodeId AgentId, string Series)>> ListSeriesAsync(long fromMs, long toMs, CancellationToken ct);
|
|
|
|
/// <summary>启动时截断没有索引记录的段文件尾部。</summary>
|
|
ValueTask RecoverAsync(CancellationToken ct);
|
|
|
|
long TotalBytes { get; }
|
|
}
|
|
|
|
public interface IMetricStore
|
|
{
|
|
/// <summary>覆盖写入(recompute 路径)。</summary>
|
|
ValueTask UpsertAsync(MetricLevel level, IReadOnlyList<MetricRow> rows, CancellationToken ct);
|
|
|
|
/// <summary>读取—合并—写回(流式路径:同一个窗口由多个块累加)。</summary>
|
|
ValueTask MergeAsync(MetricLevel level, IReadOnlyList<MetricRow> rows, CancellationToken ct);
|
|
|
|
/// <summary>用新算出的指标替换已有行里的同名指标,其余指标保持不变(回填新增或变更的指标)。</summary>
|
|
ValueTask PatchAsync(MetricLevel level, IReadOnlyList<MetricRow> rows, IReadOnlySet<string> replaceMetricIds, CancellationToken ct);
|
|
|
|
ValueTask<IReadOnlyList<MetricRow>> ReadAsync(MetricLevel level, NodeId agentId, string series, long fromMs, long toMs, CancellationToken ct);
|
|
|
|
/// <summary>这条序列在某一层里最早的窗口起点;没有数据返回 null。</summary>
|
|
ValueTask<long?> EarliestAsync(MetricLevel level, NodeId agentId, string series, CancellationToken ct);
|
|
|
|
ValueTask<IReadOnlyList<MetricRow>> ReadAgentsAsync(MetricLevel level, IReadOnlyCollection<NodeId> agentIds, string series, long fromMs, long toMs, CancellationToken ct);
|
|
|
|
ValueTask<int> DeleteOlderThanAsync(MetricLevel level, long cutoffMs, CancellationToken ct);
|
|
|
|
ValueTask EnqueueAsync(IReadOnlyList<QueueItem> items, CancellationToken ct);
|
|
|
|
ValueTask<IReadOnlyList<QueueItem>> DequeueDueAsync(long nowMs, int limit, CancellationToken ct);
|
|
|
|
ValueTask CompleteAsync(IReadOnlyList<QueueItem> items, CancellationToken ct);
|
|
|
|
ValueTask<int> QueueLengthAsync(CancellationToken ct);
|
|
|
|
ValueTask MarkRollupDirtyAsync(MetricLevel level, NodeId agentId, string series, long bucketStart, CancellationToken ct);
|
|
|
|
ValueTask<IReadOnlyList<(MetricLevel Level, NodeId AgentId, string Series, long BucketStart)>> TakeRollupDirtyAsync(int limit, CancellationToken ct);
|
|
|
|
ValueTask<IReadOnlyList<MetricVersionRecord>> GetVersionsAsync(CancellationToken ct);
|
|
|
|
ValueTask SetVersionAsync(MetricVersionRecord version, CancellationToken ct);
|
|
}
|
|
|
|
public sealed record StorageHealth(bool Database, bool RawDirectory);
|
|
|
|
/// <summary>/healthz 用:SQLite 与原始数据目录都可写。</summary>
|
|
public interface IStorageHealth
|
|
{
|
|
ValueTask<StorageHealth> CheckAsync(CancellationToken ct);
|
|
}
|