1use std::time::Duration;
7use actix::clock::sleep;
10use actix::spawn;
11use tokio::sync::oneshot::{channel, error::TryRecvError, Receiver, Sender};
12use tokio::task::JoinHandle;
13
14use crate::api::v1::mcaptcha::easy::{
15 update_runner, TrafficPatternRequest, UpdateTrafficPattern,
16};
17use crate::*;
18
19use errors::*;
20
21pub struct UpdateEasyCaptcha {
22 tx: Sender<()>,
23}
24
25impl UpdateEasyCaptcha {
26 pub async fn spawn(
27 data: AppData,
28 duration: u32,
29 ) -> ServiceResult<(Self, JoinHandle<()>)> {
30 let (tx, rx) = channel();
31 let handle = Self::run(data, duration, rx).await?;
32 let d = Self { tx };
33
34 Ok((d, handle))
35 }
36
37 #[allow(dead_code)]
38 pub fn abort(mut self) {
39 self.tx.send(());
40 }
41
42 async fn update_captcha_configurations(
44 data: &AppData,
45 rx: &mut Receiver<()>,
46 ) -> ServiceResult<()> {
47 let limit = 10;
48 let mut offset = 0;
49 let mut page = 0;
50 loop {
51 offset = page * limit;
52
53 if !Self::can_run(rx) {
54 return Ok(());
55 }
56
57 let mut patterns = data.db.get_all_easy_captchas(limit, offset).await?;
58 if patterns.is_empty() {
59 break;
60 }
61 for pattern in patterns.drain(0..) {
62 if !Self::can_run(rx) {
63 return Ok(());
64 }
65
66 let publish_benchmarks =
67 data.db.analytics_captcha_is_published(&pattern.key).await?;
68
69 let req = UpdateTrafficPattern {
70 pattern: TrafficPatternRequest {
71 avg_traffic: pattern.traffic_pattern.avg_traffic,
72 peak_sustainable_traffic: pattern
73 .traffic_pattern
74 .peak_sustainable_traffic,
75 broke_my_site_traffic: pattern
76 .traffic_pattern
77 .broke_my_site_traffic,
78 description: pattern.description,
79 publish_benchmarks,
80 },
81 key: pattern.key,
82 };
83 if !Self::can_run(rx) {
84 return Ok(());
85 }
86
87 update_runner(&data, req, pattern.username).await?;
88 }
89 page += 1;
90 }
91 Ok(())
92 }
93
94 fn can_run(rx: &mut Receiver<()>) -> bool {
95 match rx.try_recv() {
96 Err(TryRecvError::Empty) => true,
97 _ => false,
98 }
99 }
100
101 pub async fn run(
102 data: AppData,
103 duration: u32,
104 mut rx: Receiver<()>,
105 ) -> ServiceResult<JoinHandle<()>> {
106 let mut exit = false;
107 let fut = async move {
108 loop {
109 if exit {
110 break;
111 }
112 for _ in 0..duration {
113 if Self::can_run(&mut rx) {
114 sleep(Duration::new(1, 0)).await;
115 continue;
116 } else {
117 exit = true;
118 break;
119 }
120 }
121
122 if let Some(err) = Self::update_captcha_configurations(&data, &mut rx)
123 .await
124 .err()
125 {
126 log::error!(
127 "Tried to update easy captcha configurations in background {:?}",
128 err
129 );
130 }
131 }
132 };
133 let handle = spawn(fut);
134 Ok(handle)
135 }
136}