feat: daily node restart controlled from the admin, connect timings as a default line task [CI]
ci/woodpecker/push/main-amd64-build Pipeline was successful

Agents and Probes exit in order at a configured time (default 04:30 UTC+8, switch and time zone in the admin settings) and the service manager starts them again; a local restart_at or MOSHIRO_RESTART_AT wins. The connect kind (TCP connect and TLS handshake timings) is now a default continuous line template, with a section on the line page.
This commit is contained in:
justme committed 2026-10-07 14:56:50 +08:00
1 parent 388619b8b6
commit 9bcef56497
37 files changed
+928 -101

No files matched your search

@@ -40,23 +40,35 @@ public sealed class TaskService
public ValueTask<TaskRecord?> GetAsync(TaskId id, CancellationToken ct) => _tasks.GetAsync(id, ct);
/// <summary>首次启动时创建默认的线路任务模板(udp_echo、tcp_echo 持续运行),这样新 Agent 注册后自动获得任务。</summary>
/// <summary>
/// 创建默认的线路任务模板,这样新 Agent 注册后自动获得任务:udp_echo、tcp_echo 持续运行,connect(TCP 建连和 TLS 握手耗时,
/// 每分钟 6 次)也持续运行。每一批默认任务只种一次,用设置里的标记记住;你删掉的默认模板不会被重新建出来,
/// 后来新增的默认任务(connect)在已有部署升级后会补建一次。
/// </summary>
public async ValueTask SeedDefaultsAsync(CancellationToken ct)
{
if (await _settings.GetAsync("tasks.seeded", ct) is not null)
if (await _settings.GetAsync("tasks.seeded", ct) is null)
{
return;
await SeedAsync([Kinds.UdpEcho, Kinds.TcpEcho], ct);
await _settings.SetAsync("tasks.seeded", "1", ct);
}
if (await _settings.GetAsync("tasks.seeded.connect", ct) is null)
{
await SeedAsync([Kinds.Connect], ct);
await _settings.SetAsync("tasks.seeded.connect", "1", ct);
}
}
private async ValueTask SeedAsync(KindSpec[] kinds, CancellationToken ct)
{
long now = _time.GetUtcNow().ToUnixTimeMilliseconds();
foreach (KindSpec kind in new[] { Kinds.UdpEcho, Kinds.TcpEcho })
foreach (KindSpec kind in kinds)
{
await _tasks.CreateAsync(
new TaskRecord(new TaskId(Ulid.NewPrefixed(TaskId.Prefix, _time)), EffectiveTasks.LineScope, null, null, kind.Id, null, new Dictionary<string, JsonElement>(), TaskSchedule.Continuous, true, now, 0),
ct);
}
await _settings.SetAsync("tasks.seeded", "1", ct);
}
public async ValueTask<Result<TaskRecord>> CreateAsync(TaskInput input, CancellationToken ct)