mirror of
https://github.com/tauri-apps/plugins-workspace.git
synced 2026-07-30 17:48:50 +02:00
feat(updater): add download and install js binding (#1330)
This commit is contained in:
@@ -5,7 +5,7 @@
|
||||
use crate::{Result, Update, UpdaterExt};
|
||||
|
||||
use serde::Serialize;
|
||||
use tauri::{ipc::Channel, Manager, ResourceId, Runtime, Webview};
|
||||
use tauri::{ipc::Channel, Manager, Resource, ResourceId, Runtime, Webview};
|
||||
|
||||
use std::time::Duration;
|
||||
use url::Url;
|
||||
@@ -35,6 +35,9 @@ pub(crate) struct Metadata {
|
||||
body: Option<String>,
|
||||
}
|
||||
|
||||
struct DownloadedBytes(pub Vec<u8>);
|
||||
impl Resource for DownloadedBytes {}
|
||||
|
||||
#[tauri::command]
|
||||
pub(crate) async fn check<R: Runtime>(
|
||||
webview: Webview<R>,
|
||||
@@ -75,6 +78,46 @@ pub(crate) async fn check<R: Runtime>(
|
||||
Ok(metadata)
|
||||
}
|
||||
|
||||
#[tauri::command]
|
||||
pub(crate) async fn download<R: Runtime>(
|
||||
webview: Webview<R>,
|
||||
rid: ResourceId,
|
||||
on_event: Channel,
|
||||
) -> Result<ResourceId> {
|
||||
let update = webview.resources_table().get::<Update>(rid)?;
|
||||
let mut first_chunk = true;
|
||||
let bytes = update
|
||||
.download(
|
||||
|chunk_length, content_length| {
|
||||
if first_chunk {
|
||||
first_chunk = !first_chunk;
|
||||
let _ = on_event.send(DownloadEvent::Started { content_length });
|
||||
}
|
||||
let _ = on_event.send(DownloadEvent::Progress { chunk_length });
|
||||
},
|
||||
|| {
|
||||
let _ = on_event.send(&DownloadEvent::Finished);
|
||||
},
|
||||
)
|
||||
.await?;
|
||||
Ok(webview.resources_table().add(DownloadedBytes(bytes)))
|
||||
}
|
||||
|
||||
#[tauri::command]
|
||||
pub(crate) async fn install<R: Runtime>(
|
||||
webview: Webview<R>,
|
||||
update_rid: ResourceId,
|
||||
bytes_rid: ResourceId,
|
||||
) -> Result<()> {
|
||||
let update = webview.resources_table().get::<Update>(update_rid)?;
|
||||
let bytes = webview
|
||||
.resources_table()
|
||||
.get::<DownloadedBytes>(bytes_rid)?;
|
||||
update.install(&bytes.0)?;
|
||||
let _ = webview.resources_table().close(bytes_rid);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[tauri::command]
|
||||
pub(crate) async fn download_and_install<R: Runtime>(
|
||||
webview: Webview<R>,
|
||||
|
||||
@@ -179,7 +179,9 @@ impl Builder {
|
||||
})
|
||||
.invoke_handler(tauri::generate_handler![
|
||||
commands::check,
|
||||
commands::download_and_install
|
||||
commands::download,
|
||||
commands::install,
|
||||
commands::download_and_install,
|
||||
])
|
||||
.build()
|
||||
}
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
use std::{
|
||||
collections::HashMap,
|
||||
ffi::{OsStr, OsString},
|
||||
io::{Cursor, Read},
|
||||
io::Cursor,
|
||||
path::{Path, PathBuf},
|
||||
str::FromStr,
|
||||
sync::Arc,
|
||||
@@ -478,19 +478,16 @@ impl Update {
|
||||
on_chunk(chunk.len(), content_length);
|
||||
buffer.extend(chunk);
|
||||
}
|
||||
|
||||
on_download_finish();
|
||||
|
||||
let mut update_buffer = Cursor::new(&buffer);
|
||||
|
||||
verify_signature(&mut update_buffer, &self.signature, &self.config.pubkey)?;
|
||||
verify_signature(&buffer, &self.signature, &self.config.pubkey)?;
|
||||
|
||||
Ok(buffer)
|
||||
}
|
||||
|
||||
/// Installs the updater package downloaded by [`Update::download`]
|
||||
pub fn install(&self, bytes: Vec<u8>) -> Result<()> {
|
||||
self.install_inner(bytes)
|
||||
pub fn install(&self, bytes: impl AsRef<[u8]>) -> Result<()> {
|
||||
self.install_inner(bytes.as_ref())
|
||||
}
|
||||
|
||||
/// Downloads and installs the updater package
|
||||
@@ -504,7 +501,7 @@ impl Update {
|
||||
}
|
||||
|
||||
#[cfg(mobile)]
|
||||
fn install_inner(&self, _bytes: Vec<u8>) -> Result<()> {
|
||||
fn install_inner(&self, _bytes: &[u8]) -> Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
@@ -546,13 +543,13 @@ impl Update {
|
||||
/// ├── [AppName]_[version]_x64-setup.exe.zip # ZIP generated by tauri-bundler
|
||||
/// │ └──[AppName]_[version]_x64-setup.exe # NSIS installer
|
||||
/// └── ...
|
||||
fn install_inner(&self, bytes: Vec<u8>) -> Result<()> {
|
||||
fn install_inner(&self, bytes: &[u8]) -> Result<()> {
|
||||
use windows_sys::{
|
||||
w,
|
||||
Win32::UI::{Shell::ShellExecuteW, WindowsAndMessaging::SW_SHOW},
|
||||
};
|
||||
|
||||
let (updater_type, path, _temp) = Self::extract(&bytes)?;
|
||||
let (updater_type, path, _temp) = Self::extract(bytes)?;
|
||||
|
||||
let install_mode = self.config.install_mode();
|
||||
let mut installer_args = self.installer_args();
|
||||
@@ -666,7 +663,7 @@ impl Update {
|
||||
/// We should have an AppImage already installed to be able to copy and install
|
||||
/// the extract_path is the current AppImage path
|
||||
/// tmp_dir is where our new AppImage is found
|
||||
fn install_inner(&self, bytes: Vec<u8>) -> Result<()> {
|
||||
fn install_inner(&self, bytes: &[u8]) -> Result<()> {
|
||||
use std::os::unix::fs::{MetadataExt, PermissionsExt};
|
||||
let extract_path_metadata = self.extract_path.metadata()?;
|
||||
|
||||
@@ -694,7 +691,7 @@ impl Update {
|
||||
std::fs::rename(&self.extract_path, tmp_app_image)?;
|
||||
|
||||
#[cfg(feature = "zip")]
|
||||
if infer::archive::is_gz(&bytes) {
|
||||
if infer::archive::is_gz(bytes) {
|
||||
// extract the buffer to the tmp_dir
|
||||
// we extract our signed archive into our final directory without any temp file
|
||||
let archive = Cursor::new(bytes);
|
||||
@@ -743,7 +740,7 @@ impl Update {
|
||||
/// │ └── Contents # Application contents...
|
||||
/// │ └── ...
|
||||
/// └── ...
|
||||
fn install_inner(&self, bytes: Vec<u8>) -> Result<()> {
|
||||
fn install_inner(&self, bytes: &[u8]) -> Result<()> {
|
||||
use flate2::read::GzDecoder;
|
||||
|
||||
let cursor = Cursor::new(bytes);
|
||||
@@ -919,30 +916,15 @@ where
|
||||
}
|
||||
|
||||
// Validate signature
|
||||
// need to be public because its been used
|
||||
// by our tests in the bundler
|
||||
//
|
||||
// NOTE: The buffer position is not reset.
|
||||
pub fn verify_signature<R>(
|
||||
archive_reader: &mut R,
|
||||
release_signature: &str,
|
||||
pub_key: &str,
|
||||
) -> Result<bool>
|
||||
where
|
||||
R: Read,
|
||||
{
|
||||
fn verify_signature(data: &[u8], release_signature: &str, pub_key: &str) -> Result<bool> {
|
||||
// we need to convert the pub key
|
||||
let pub_key_decoded = base64_to_string(pub_key)?;
|
||||
let public_key = PublicKey::decode(&pub_key_decoded)?;
|
||||
let signature_base64_decoded = base64_to_string(release_signature)?;
|
||||
let signature = Signature::decode(&signature_base64_decoded)?;
|
||||
|
||||
// read all bytes until EOF in the buffer
|
||||
let mut data = Vec::new();
|
||||
archive_reader.read_to_end(&mut data)?;
|
||||
|
||||
// Validate signature or bail out
|
||||
public_key.verify(&data, &signature, true)?;
|
||||
public_key.verify(data, &signature, true)?;
|
||||
Ok(true)
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user