Skip to main content

max / makenotwork

3.0 KB · 87 lines History Blame Raw
1 //! Background CORS preflight check task.
2
3 use tokio::task::JoinHandle;
4 use tracing::info;
5
6 use pom::alerts::Alerter;
7 use pom::checks::cors;
8 use pom::config::Config;
9 use pom::db;
10
11 use super::{CheckInterval, configured_targets};
12
13 pub(crate) fn spawn_cors_tasks(
14 config: &Config,
15 pool: &sqlx::SqlitePool,
16 cancel: &tokio_util::sync::CancellationToken,
17 alerter: Option<&Alerter>,
18 ) -> Vec<JoinHandle<()>> {
19 let interval_secs = config.serve.cors_check_interval_secs;
20 let mut handles = Vec::new();
21
22 for (name, target_config) in configured_targets(config) {
23 if target_config.cors.is_empty() {
24 continue;
25 }
26 let cors_checks = target_config.cors.clone();
27 let label = target_config.label.clone();
28 let pool = pool.clone();
29 let alerter = alerter.cloned();
30 let cancel = cancel.clone();
31 let n = cors_checks.len();
32
33 info!("{name}: CORS check every {interval_secs}s ({n} endpoints)");
34
35 handles.push(tokio::spawn(async move {
36 let mut ticks = CheckInterval::new(interval_secs, cancel);
37 let mut prev_failed: std::collections::HashSet<String> =
38 std::collections::HashSet::new();
39
40 while ticks.next().await {
41 let results = cors::check_cors(&name, &cors_checks).await;
42
43 for result in &results {
44 if let Err(e) = db::insert_cors_check(&pool, result).await {
45 tracing::error!(
46 "{}: failed to store CORS check for {}: {e}",
47 name,
48 result.url
49 );
50 }
51 }
52
53 let current_failed: std::collections::HashSet<String> = results
54 .iter()
55 .filter(|r| !r.passes)
56 .map(|r| r.url.clone())
57 .collect();
58
59 let ok_count = results.iter().filter(|r| r.passes).count();
60 info!("{name}: CORS {ok_count}/{n} pass");
61
62 if let Some(ref alerter) = alerter {
63 // New failures
64 let new_failures: Vec<&pom::types::CorsCheckResult> = results
65 .iter()
66 .filter(|r| !r.passes && !prev_failed.contains(&r.url))
67 .collect();
68 if !new_failures.is_empty() {
69 let owned: Vec<pom::types::CorsCheckResult> =
70 new_failures.into_iter().cloned().collect();
71 alerter.send_cors_failure_alert(&name, &label, &owned).await;
72 }
73
74 // All recovered
75 if !prev_failed.is_empty() && current_failed.is_empty() {
76 alerter.send_cors_recovery_alert(&name, &label).await;
77 }
78 }
79
80 prev_failed = current_failed;
81 }
82 }));
83 }
84
85 handles
86 }
87