📄
Program.cs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
using BfiMonitor; using HuaweiWifiSms.Grpc; using Microsoft.Extensions.Options; using Quartz; var builder = WebApplication.CreateBuilder(args); builder.ConfigureOpenTelemetry(); builder.Services.AddOptions<MonitorOptions>().BindConfiguration("Monitor"); builder.Services.AddGrpcClient<SmsSender.SmsSenderClient>( (sp, o) => { var opts = sp.GetRequiredService<IOptions<MonitorOptions>>().Value; o.Address = new Uri(opts.SmsSenderAddress); } ); builder.Services.AddSingleton<PlaywrightBrowserService>(); builder.Services.AddHostedService(sp => sp.GetRequiredService<PlaywrightBrowserService>()); builder.Services.AddSingleton<ScreeningRepository>(); builder.Services.AddSingleton(TimeProvider.System); var intervalSeconds = builder.Configuration.GetValue("Monitor:IntervalSeconds", 300); builder.Services.AddQuartz(q => { var checkerKey = JobKey.Create(nameof(BfiScreeningCheckerJob)); q.AddJob<BfiScreeningCheckerJob>(checkerKey); q.AddTrigger(t => t.ForJob(checkerKey) .StartNow() .WithSimpleSchedule(s => s.WithIntervalInSeconds(intervalSeconds).RepeatForever()) ); q.AddJob<SendSmsJob>(SendSmsJob.Key, j => j.StoreDurably()); }); builder.Services.AddQuartzHostedService(o => o.WaitForJobsToComplete = true); var app = builder.Build(); app.UseDefaultFiles(); app.UseStaticFiles(); app.MapGet( "/api/status", async (IOptions<MonitorOptions> options, ScreeningRepository repository, CancellationToken ct) => { var opts = options.Value; var latest = await repository.GetDetectionsAsync(1, ct); return Results.Json( new { opts.Url, opts.Selector, opts.IntervalSeconds, opts.PhoneNumbers, TotalDetections = await repository.CountDetectionsAsync(ct), LatestDetectionAt = latest.FirstOrDefault()?.DetectedAt, } ); } ); app.MapGet( "/api/detections", async (ScreeningRepository repository, CancellationToken ct, int limit = 50) => { var detections = await repository.GetDetectionsAsync(Math.Clamp(limit, 1, 200), ct); return Results.Json( detections.Select(d => new { d.Id, d.DetectedAt, Preview = d.Html.Length > 200 ? d.Html[..200] + "…" : d.Html, }) ); } ); app.MapGet( "/api/detections/{id:int}", async (int id, ScreeningRepository repository, CancellationToken ct) => { var detection = await repository.GetDetectionByIdAsync(id, ct); return detection is null ? Results.NotFound() : Results.Json(detection); } ); app.Run();