summary refs log tree commit diff
path: root/app/src/lib.rs
blob: 8ad149f43537dc2832e3d462fc2275cf636a2c30 (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
132
133
134
135
136
mod oauth;
use oauth::OAuthController;

use tauri_plugin_deep_link::DeepLinkExt;
use tauri_plugin_opener::OpenerExt;

use tauri::{Manager, State, AppHandle};
use url::Host;
use uuid::Uuid;

pub const OAUTH_CLIENT_NAME: &'static str = "foxfleet_test";

struct AppState {
    oauth_controller: OAuthController
}

#[cfg_attr(mobile, tauri::mobile_entry_point)]
pub fn run() {
    tauri::Builder::default()
        .plugin(tauri_plugin_single_instance::init(|app, argv, cwd| {
            println!("{}, {argv:?}, {cwd}", app.package_info().name);
        }))
        .plugin(tauri_plugin_deep_link::init())
        .plugin(tauri_plugin_opener::init())
        .setup(|app| {
            app.manage(AppState {
                oauth_controller: OAuthController::new()
            });

            #[cfg(any(target_os = "linux", all(debug_assertions, windows)))]
            app.deep_link().register_all()?;
            let app_handle = app.handle().clone();
            app.deep_link().on_open_url(move |event| {
                if let Some(oauth_callback) = event.urls().iter().find(|url| {
                    if let Some(Host::Domain(domain)) = url.host() {
                        domain == "oauth-response"
                    } else {
                        false
                    }
                }) {
                    let mut query = oauth_callback.query_pairs();
                    let query = (
                        query.find(|(key, _)| key == "code").map(|(_, value)| value ),
                        query.find(|(key, _)| key == "state").map(|(_, value)| value )
                    );
                    let query = match query {
                        (Some(code), Some(state)) => Some((code, state)),
                        _ => None,
                    };

                    if let Some((auth_code, state)) = query {
                        if let Ok(state) = Uuid::try_parse(&state) {
                            let state = state.clone();
                            let auth_code = auth_code.to_string().clone();
                            let app_handle = app_handle.clone();
                            let oauth_controller = app_handle.state::<AppState>().oauth_controller.clone();

                            tauri::async_runtime::spawn(async move {
                                oauth_controller.resolve_code(state, auth_code);
                            });
                        } else {
                            println!("Invalid UUID format: {state}");
                            return
                        }
                    } else {
                        println!("Missing either state or code in oauth callback");
                        return
                    }
                }
            });
            Ok(())
        })
        .invoke_handler(tauri::generate_handler![start_account_auth, get_self])
        .run(tauri::generate_context!())
        .expect("Error starting")
}


#[tauri::command]
async fn start_account_auth(app_handle: AppHandle, state: State<'_, AppState>, instance_domain: &str) -> Result<Vec<String>, ()> {
    let add_result = state.oauth_controller.add_server(instance_domain).await;

    let state_nonce = match add_result {
        Ok(result) => {
            let opener = app_handle.opener();
            if let Err(_) = opener.open_url(result.auth_url, None::<&str>) {
                println!("Could not open authentication page");
                return Err(())
            }

            result.auth_state
        }
        Err(err) => {
            println!("Error adding server: {err}");
            return Err(())
        }
    };

    let signin_result = state.oauth_controller.finish_signin(state_nonce).await;
    match signin_result {
        Ok((server_domain, username)) => {
            println!("Signed in successfully");
            Ok(vec!(server_domain, username))
        }
        Err(err) => {
            println!("Error completing signin: {err}");
            Err(())
        }
    }
}

#[tauri::command]
async fn get_self(state: State<'_, AppState>, server_domain: String, username: String) -> Result<String, String> {
    let client = reqwest::Client::builder().user_agent("Foxfleet v0.0.1").build().expect("Could not construct client");

    let api_key = state.oauth_controller.get_api_token(server_domain, username).await;
    match api_key {
        Err(err) => {
            println!("Error getting API token: {err}");
            return Err(err)
        }
        Ok(api_key) => {
            if let Ok(result) = client.get("https://social.tempest.dev/api/v1/accounts/verify_credentials")
                .bearer_auth(api_key)
                .send().await {
                    if let Ok(result) = result.text().await {
                        return Ok(result)
                    } else {
                        return Err("Error decoding response".to_string());
                    }
                } else {
                    return Err("Error fetching account".to_string());
                }
        }
    }
}