aboutsummaryrefslogtreecommitdiff
path: root/src-tauri/src/github/release_notes.rs
blob: cbe3d0b132a9120a3bac03a7658caac00eef2c88 (plain)
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
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
use crate::constants::APP_USER_AGENT;
use serde::{Deserialize, Serialize};
use std::vec::Vec;
use ts_rs::TS;

#[derive(Serialize, Deserialize, Debug, Clone, TS)]
#[ts(export)]
pub struct ReleaseInfo {
    pub name: String,
    pub published_at: String,
    pub body: String,
}

#[derive(Serialize, Deserialize, Debug, Clone, TS)]
#[ts(export)]
pub struct FlightCoreVersion {
    tag_name: String,
    published_at: String,
}

// Fetches repo release API and returns response as string
pub async fn fetch_github_releases_api(url: &str) -> Result<String, anyhow::Error> {
    log::info!("Fetching releases notes from GitHub API");

    let client = reqwest::Client::new();
    let res = client
        .get(url)
        .header(reqwest::header::USER_AGENT, APP_USER_AGENT)
        .send()
        .await?
        .text()
        .await?;

    Ok(res)
}

/// Gets newest FlighCore version from GitHub
#[tauri::command]
pub async fn get_newest_flightcore_version() -> Result<FlightCoreVersion, String> {
    // Get newest version number from GitHub API
    log::info!("Checking GitHub API");
    let url = "https://api.github.com/repos/R2NorthstarTools/FlightCore/releases/latest";
    let res = match fetch_github_releases_api(url).await {
        Ok(res) => res,
        Err(err) => return Err(format!("Failed getting newest FlightCore version: {err}")),
    };

    let flightcore_version: FlightCoreVersion =
        serde_json::from_str(&res).expect("JSON was not well-formatted");
    log::info!("Done checking GitHub API");

    Ok(flightcore_version)
}

/// Checks if installed FlightCore version is up-to-date
/// false -> FlightCore install is up-to-date
/// true  -> FlightCore install is outdated
#[tauri::command]
pub async fn check_is_flightcore_outdated() -> Result<bool, String> {
    let newest_flightcore_release = get_newest_flightcore_version().await?;
    // Parse version number excluding leading `v`
    let newest_version = semver::Version::parse(&newest_flightcore_release.tag_name[1..]).unwrap();

    // Get version of installed FlightCore
    let current_version = env!("CARGO_PKG_VERSION");
    let current_version = semver::Version::parse(current_version).unwrap();

    #[cfg(debug_assertions)]
    let is_outdated = current_version < newest_version;
    #[cfg(not(debug_assertions))]
    let is_outdated = current_version != newest_version;

    // If outdated, check how new the update is
    if is_outdated {
        // Time to wait (2h)    h *  m *  s
        let threshold_seconds = 2 * 60 * 60;

        // Get current time
        let current_time = chrono::Utc::now();

        // Get latest release time from GitHub API response
        let result = chrono::DateTime::parse_from_rfc3339(&newest_flightcore_release.published_at)
            .unwrap()
            .with_timezone(&chrono::Utc);

        // Check if current time is outside of threshold
        let diff = current_time - result;
        if diff.num_seconds() < threshold_seconds {
            // User would be outdated but the newest release is recent
            // therefore we do not wanna show outdated warning.
            return Ok(false);
        }
        return Ok(true);
    }

    Ok(is_outdated)
}

#[tauri::command]
pub async fn get_northstar_release_notes() -> Result<Vec<ReleaseInfo>, String> {
    let octocrab = octocrab::instance();
    let page = octocrab
        .repos("R2Northstar", "Northstar")
        .releases()
        .list()
        // Optional Parameters
        .per_page(25)
        .page(1u32)
        // Send the request
        .send()
        .await
        .unwrap();

    // TODO there's probably a way to automatically serialize into the struct but I don't know yet how to
    let mut release_info_vector: Vec<ReleaseInfo> = vec![];
    for item in page.items {
        let release_info = ReleaseInfo {
            name: item.name.ok_or(String::from("Release name not found"))?,
            published_at: item
                .published_at
                .ok_or(String::from("Release date not found"))?
                .to_rfc3339(),
            body: item.body.ok_or(String::from("Release body not found"))?,
        };
        release_info_vector.push(release_info);
    }

    log::info!("Done checking GitHub API");

    Ok(release_info_vector)
}