rhombus 0.2.21

Next generation extendable CTF framework with batteries included
Documentation
use std::time::Duration;

use chrono::{TimeZone, Utc};

use crate::internal::database::provider::{Connection, WeakConnection};

pub async fn healthcheck_catch_up(db: Connection) {
    tracing::info!("Running healthcheck catch-up");
    let challenges = db.get_challenges().await.unwrap();

    for challenge in &challenges.challenges {
        if challenge.healthscript.is_none() {
            continue;
        }

        if challenge.healthy.is_some() {
            continue;
        }

        let (expr, _) = healthscript::parse(challenge.healthscript.as_ref().unwrap());
        let healthy = if let Some(expr) = expr {
            Some(expr.execute().await.0)
        } else {
            tracing::error!(challenge_id = challenge.id, "Failed to parse healthscript");
            None
        };

        _ = db
            .set_challenge_health(challenge.id, healthy, chrono::Utc::now())
            .await;

        tracing::trace!(challenge_id = challenge.id, challenge_name = challenge.name, healthy = ?healthy, "Healthcheck catch-up");
    }
}

pub fn healthcheck_runner(db: WeakConnection) {
    tokio::task::spawn(async move {
        loop {
            tokio::time::sleep(Duration::from_secs(30)).await;

            let Some(db) = db.upgrade() else {
                break;
            };
            let Ok(challenges) = db.get_challenges().await else {
                break;
            };

            if let Some(challenge) = challenges
                .challenges
                .iter()
                .filter(|challenge| challenge.healthscript.is_some())
                .min_by_key(|challenge| {
                    challenge
                        .last_healthcheck
                        .unwrap_or(Utc.timestamp_opt(0, 0).unwrap())
                })
            {
                let (expr, _) = healthscript::parse(challenge.healthscript.as_ref().unwrap());
                let healthy = if let Some(expr) = expr {
                    Some(expr.execute().await.0)
                } else {
                    tracing::error!(challenge_id = challenge.id, "Failed to parse healthscript");
                    None
                };

                _ = db
                    .set_challenge_health(challenge.id, healthy, chrono::Utc::now())
                    .await;

                tracing::trace!(challenge_id = challenge.id, challenge_name = challenge.name, healthy = ?healthy, "Healthcheck");
            }
        }
    });
}