Skip to main content

tauri_plugin_keygen_rs2/
lib.rs

1use error::Error;
2use keygen_rs::{config::KeygenConfig, license_file::LicenseFile, machine_file::MachineFile};
3use lazy_static::lazy_static;
4use license::LicenseState;
5use machine::MachineState;
6use tauri::{
7    plugin::{Builder as PluginBuilder, TauriPlugin},
8    Manager, Runtime, State,
9};
10use tokio::sync::Mutex;
11
12mod commands;
13pub mod error;
14pub mod license;
15pub mod machine;
16mod utils;
17
18pub use keygen_rs;
19pub use keygen_rs::{component::Component, entitlement::Entitlement, machine::Machine};
20
21pub type Result<T> = std::result::Result<T, Error>;
22
23lazy_static! {
24    static ref LISTENERS: Mutex<Vec<Box<dyn Fn(&LicenseState) + Send + Sync + 'static>>> =
25        Mutex::new(Vec::new());
26}
27
28pub async fn add_license_listener<F>(listener: F)
29where
30    F: Fn(&LicenseState) + Send + Sync + 'static,
31{
32    let mut listeners = LISTENERS.lock().await;
33    listeners.push(Box::new(listener));
34}
35
36pub(crate) async fn notify_license_listeners(state: &LicenseState) {
37    let listeners = LISTENERS.lock().await;
38    for listener in listeners.iter() {
39        listener(state);
40    }
41}
42
43pub trait AppHandleExt {
44    fn get_keygen_config(&self) -> State<'_, Mutex<KeygenConfig>>;
45    fn get_license_state(&self) -> State<'_, Mutex<LicenseState>>;
46    fn load_license_file(&self, key: &str) -> Result<Option<LicenseFile>>;
47    fn remove_license_file(&self) -> Result<()>;
48    fn get_machine_state(&self) -> State<'_, Mutex<MachineState>>;
49    fn load_machine_file(&self, key: &str) -> Result<Option<MachineFile>>;
50    fn remove_machine_file(&self) -> Result<()>;
51}
52
53impl<R: Runtime> AppHandleExt for tauri::AppHandle<R> {
54    fn get_keygen_config(&self) -> State<'_, Mutex<KeygenConfig>> {
55        self.state::<Mutex<KeygenConfig>>()
56    }
57
58    fn get_license_state(&self) -> State<'_, Mutex<LicenseState>> {
59        self.state::<Mutex<LicenseState>>()
60    }
61
62    fn load_license_file(&self, key: &str) -> Result<Option<LicenseFile>> {
63        LicenseState::load_license_file(self, key)
64    }
65
66    fn remove_license_file(&self) -> Result<()> {
67        LicenseState::remove_license_file(self)
68    }
69
70    fn get_machine_state(&self) -> State<'_, Mutex<MachineState>> {
71        self.state::<Mutex<MachineState>>()
72    }
73
74    fn load_machine_file(&self, key: &str) -> Result<Option<MachineFile>> {
75        MachineState::load_machine_file(self, key)
76    }
77
78    fn remove_machine_file(&self) -> Result<()> {
79        MachineState::remove_machine_file(self)
80    }
81}
82
83pub struct Builder {
84    account: String,
85    product: String,
86    public_key: String,
87    api_url: Option<String>,
88    api_version: Option<String>,
89    api_prefix: Option<String>,
90}
91
92impl Builder {
93    pub fn new(
94        account: impl Into<String>,
95        product: impl Into<String>,
96        public_key: impl Into<String>,
97    ) -> Self {
98        Self {
99            account: account.into(),
100            product: product.into(),
101            public_key: public_key.into(),
102            api_url: None,
103            api_version: None,
104            api_prefix: None,
105        }
106    }
107
108    pub fn api_url(mut self, api_url: impl Into<String>) -> Self {
109        self.api_url = Some(api_url.into());
110        self
111    }
112
113    pub fn api_version(mut self, api_version: impl Into<String>) -> Self {
114        self.api_version = Some(api_version.into());
115        self
116    }
117
118    pub fn api_prefix(mut self, api_prefix: impl Into<String>) -> Self {
119        self.api_prefix = Some(api_prefix.into());
120        self
121    }
122
123    pub fn build<R: Runtime>(self) -> TauriPlugin<R> {
124        let config = KeygenConfig {
125            api_url: self.api_url.unwrap_or("https://api.keygen.sh".to_string()),
126            api_version: self.api_version.unwrap_or("1.7".to_string()),
127            api_prefix: self.api_prefix.unwrap_or("v1".to_string()),
128            account: self.account,
129            product: self.product,
130            public_key: Some(self.public_key),
131            ..Default::default()
132        };
133
134        PluginBuilder::new("keygen-rs2")
135            .invoke_handler(tauri::generate_handler![
136                commands::get_license,
137                commands::is_license_valid,
138                commands::get_license_key,
139                commands::validate_key,
140                commands::activate,
141                commands::deactivate,
142                commands::checkout_license,
143                commands::checkout_machine,
144                commands::reset_license,
145                commands::get_license_metadata,
146            ])
147            .setup(move |app_handle, _api| {
148                let app_name = app_handle.package_info().name.clone();
149                let app_version = app_handle.package_info().version.to_string();
150
151                let machine_state =
152                    MachineState::new(app_handle, app_name.clone(), app_version.clone());
153
154                // Create the KeygenConfig with platform and user_agent from machine state
155                let mut keygen_config = config.clone();
156                keygen_config.platform = Some(machine_state.platform.clone());
157                keygen_config.user_agent = Some(machine_state.user_agent.clone());
158
159                // Check if there's a cached license key and set it to the config
160                if let Ok(Some(cached_license_key)) =
161                    LicenseState::load_license_key_cache(app_handle)
162                {
163                    keygen_config.license_key = Some(cached_license_key);
164                }
165
166                app_handle.manage(Mutex::new(keygen_config));
167                app_handle.manage(Mutex::new(machine_state));
168
169                let license_state = LicenseState::load(app_handle);
170                match license_state {
171                    Ok(license_state) => {
172                        app_handle.manage(Mutex::new(license_state));
173                    }
174                    Err(err) => {
175                        if let Error::KeygenError(e) = err {
176                            match e {
177                                keygen_rs::errors::Error::LicenseFileExpired(dataset) => {
178                                    let license = dataset.license.clone();
179                                    let license_state = LicenseState {
180                                        key: Some(license.key.clone()),
181                                        license: Some(license),
182                                        valid: false,
183                                        included: dataset.included,
184                                    };
185                                    app_handle.manage(Mutex::new(license_state));
186                                }
187                                keygen_rs::errors::Error::MachineFileExpired(dataset) => {
188                                    let license = dataset.license.clone();
189                                    let license_state = LicenseState {
190                                        key: Some(license.key.clone()),
191                                        license: Some(license),
192                                        valid: false,
193                                        included: dataset.included,
194                                    };
195                                    app_handle.manage(Mutex::new(license_state));
196                                }
197                                _ => {
198                                    let license_state = LicenseState::default();
199                                    app_handle.manage(Mutex::new(license_state));
200                                }
201                            }
202                        }
203                    }
204                };
205
206                Ok(())
207            })
208            .build()
209    }
210}