tauri_plugin_keygen_rs2/
lib.rs1use 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 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 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}