Hide secondary windows until the login UI is painted, open external links in the default browser, and add signed Githop auto-updates
Hop-State: A_06FPJAAEMY0VXGY9S90PE90 Hop-Proposal: R_06FPJA9RY0ZAWJ1K0K760W8 Hop-Task: T_06FPJ5688NFG8VWKSFCTZEG Hop-Attempt: AT_06FPJ5688M08V2K0PEFVQX0
This commit is contained in:
@@ -35,3 +35,29 @@ cargo test --manifest-path src-tauri/Cargo.toml
|
|||||||
- The selected node's complete website experience after authentication.
|
- The selected node's complete website experience after authentication.
|
||||||
|
|
||||||
The node address and HttpOnly website session persist between launches using the native webview's cookie store.
|
The node address and HttpOnly website session persist between launches using the native webview's cookie store.
|
||||||
|
|
||||||
|
## Auto-updates
|
||||||
|
|
||||||
|
Production builds check the newest public release at
|
||||||
|
`GnosysLabs/Synapsis-Desktop` on Githop. When a newer signed release is present,
|
||||||
|
Synapsis downloads it, verifies it with the embedded updater public key, installs
|
||||||
|
it, and relaunches automatically. Development builds do not check for updates.
|
||||||
|
|
||||||
|
The encrypted updater private key lives outside this repository at
|
||||||
|
`/Users/christopher/.tauri/synapsis-desktop-updater.key`. Matching
|
||||||
|
`TAURI_SIGNING_PRIVATE_KEY` and `TAURI_SIGNING_PRIVATE_KEY_PASSWORD` secrets are
|
||||||
|
configured for the Githop repository. Never commit the private key or password.
|
||||||
|
|
||||||
|
To prepare a macOS update locally:
|
||||||
|
|
||||||
|
1. Increment `version` in `src-tauri/tauri.conf.json`, `src-tauri/Cargo.toml`, and
|
||||||
|
`package.json`.
|
||||||
|
2. Export `TAURI_SIGNING_PRIVATE_KEY` and
|
||||||
|
`TAURI_SIGNING_PRIVATE_KEY_PASSWORD` for the updater signing key.
|
||||||
|
3. Run `bun run release` to build the app, updater archive, and signature.
|
||||||
|
4. Run `bun run release:manifest` to create `latest.json` beside the archive.
|
||||||
|
5. Create a Githop release tagged `v<version>` and attach
|
||||||
|
`Synapsis.app.tar.gz`, `Synapsis.app.tar.gz.sig`, and `latest.json`.
|
||||||
|
|
||||||
|
The release tag and manifest tag must match. Override the defaults with
|
||||||
|
`SYNAPSIS_RELEASE_TAG` and `SYNAPSIS_RELEASE_NOTES` when needed.
|
||||||
|
|||||||
+3
-1
@@ -7,7 +7,9 @@
|
|||||||
"dev": "vite",
|
"dev": "vite",
|
||||||
"build": "tsc && vite build",
|
"build": "tsc && vite build",
|
||||||
"preview": "vite preview",
|
"preview": "vite preview",
|
||||||
"tauri": "tauri"
|
"tauri": "tauri",
|
||||||
|
"release": "tauri build --config src-tauri/tauri.release.conf.json",
|
||||||
|
"release:manifest": "bun scripts/create-update-manifest.mjs"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@tauri-apps/api": "^2",
|
"@tauri-apps/api": "^2",
|
||||||
|
|||||||
@@ -0,0 +1,31 @@
|
|||||||
|
import { readFile, writeFile } from "node:fs/promises";
|
||||||
|
import process from "node:process";
|
||||||
|
|
||||||
|
const repository = "https://githop.xyz/GnosysLabs/Synapsis-Desktop";
|
||||||
|
const bundleDirectory = new URL("../src-tauri/target/release/bundle/macos/", import.meta.url);
|
||||||
|
const config = JSON.parse(
|
||||||
|
await readFile(new URL("../src-tauri/tauri.conf.json", import.meta.url), "utf8"),
|
||||||
|
);
|
||||||
|
const version = config.version;
|
||||||
|
const tag = process.env.SYNAPSIS_RELEASE_TAG || `v${version}`;
|
||||||
|
const notes = process.env.SYNAPSIS_RELEASE_NOTES || `Synapsis ${version}`;
|
||||||
|
const archiveName = "Synapsis.app.tar.gz";
|
||||||
|
const signature = (
|
||||||
|
await readFile(new URL(`${archiveName}.sig`, bundleDirectory), "utf8")
|
||||||
|
).trim();
|
||||||
|
const architecture = process.arch === "arm64" ? "aarch64" : "x86_64";
|
||||||
|
const manifest = {
|
||||||
|
version,
|
||||||
|
notes,
|
||||||
|
pub_date: new Date().toISOString(),
|
||||||
|
platforms: {
|
||||||
|
[`darwin-${architecture}`]: {
|
||||||
|
signature,
|
||||||
|
url: `${repository}/releases/download/${encodeURIComponent(tag)}/${archiveName}`,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
const destination = new URL("latest.json", bundleDirectory);
|
||||||
|
await writeFile(destination, `${JSON.stringify(manifest, null, 2)}\n`);
|
||||||
|
console.log(`Created updater manifest for Synapsis ${version}.`);
|
||||||
Generated
+361
-6
@@ -47,6 +47,15 @@ version = "1.0.103"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "2a4385e2e34eb35d6b3efe798b9eb88096925d87726c0798709bf56d9ed84af3"
|
checksum = "2a4385e2e34eb35d6b3efe798b9eb88096925d87726c0798709bf56d9ed84af3"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "arbitrary"
|
||||||
|
version = "1.4.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "c3d036a3c4ab069c7b410a2ce876bd74808d2d0888a82667669f8e783a898bf1"
|
||||||
|
dependencies = [
|
||||||
|
"derive_arbitrary",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "async-broadcast"
|
name = "async-broadcast"
|
||||||
version = "0.7.2"
|
version = "0.7.2"
|
||||||
@@ -725,6 +734,17 @@ dependencies = [
|
|||||||
"serde_core",
|
"serde_core",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "derive_arbitrary"
|
||||||
|
version = "1.4.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "1e567bd82dcff979e4b03460c307b3cdc9e96fde3d73bed1496d2bc75d9dd62a"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"syn 2.0.119",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "derive_more"
|
name = "derive_more"
|
||||||
version = "2.1.1"
|
version = "2.1.1"
|
||||||
@@ -1027,6 +1047,16 @@ dependencies = [
|
|||||||
"rustc_version",
|
"rustc_version",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "filetime"
|
||||||
|
version = "0.2.29"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "5c287a33c7f0a620c38e641e7f60827713987b3c0f26e8ddc9462cc69cf75759"
|
||||||
|
dependencies = [
|
||||||
|
"cfg-if",
|
||||||
|
"libc",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "find-msvc-tools"
|
name = "find-msvc-tools"
|
||||||
version = "0.1.9"
|
version = "0.1.9"
|
||||||
@@ -1849,6 +1879,25 @@ version = "2.12.0"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "d98f6fed1fde3f8c21bc40a1abb88dd75e67924f9cffc3ef95607bad8017f8e2"
|
checksum = "d98f6fed1fde3f8c21bc40a1abb88dd75e67924f9cffc3ef95607bad8017f8e2"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "is-docker"
|
||||||
|
version = "0.2.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "928bae27f42bc99b60d9ac7334e3a21d10ad8f1835a4e12ec3ec0464765ed1b3"
|
||||||
|
dependencies = [
|
||||||
|
"once_cell",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "is-wsl"
|
||||||
|
version = "0.4.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "173609498df190136aa7dea1a91db051746d339e18476eed5ca40521f02d7aa5"
|
||||||
|
dependencies = [
|
||||||
|
"is-docker",
|
||||||
|
"once_cell",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "itoa"
|
name = "itoa"
|
||||||
version = "1.0.18"
|
version = "1.0.18"
|
||||||
@@ -1894,6 +1943,36 @@ dependencies = [
|
|||||||
"windows-sys 0.45.0",
|
"windows-sys 0.45.0",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "jni"
|
||||||
|
version = "0.22.4"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "5efd9a482cf3a427f00d6b35f14332adc7902ce91efb778580e180ff90fa3498"
|
||||||
|
dependencies = [
|
||||||
|
"cfg-if",
|
||||||
|
"combine",
|
||||||
|
"jni-macros",
|
||||||
|
"jni-sys 0.4.1",
|
||||||
|
"log",
|
||||||
|
"simd_cesu8",
|
||||||
|
"thiserror 2.0.18",
|
||||||
|
"walkdir",
|
||||||
|
"windows-link 0.2.1",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "jni-macros"
|
||||||
|
version = "0.22.4"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "a00109accc170f0bdb141fed3e393c565b6f5e072365c3bd58f5b062591560a3"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"rustc_version",
|
||||||
|
"simd_cesu8",
|
||||||
|
"syn 2.0.119",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "jni-sys"
|
name = "jni-sys"
|
||||||
version = "0.3.1"
|
version = "0.3.1"
|
||||||
@@ -2109,6 +2188,12 @@ version = "0.3.17"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "6877bb514081ee2a7ff5ef9de3281f14a4dd4bceac4c09388074a6b5df8a139a"
|
checksum = "6877bb514081ee2a7ff5ef9de3281f14a4dd4bceac4c09388074a6b5df8a139a"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "minisign-verify"
|
||||||
|
version = "0.2.5"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "22f9645cb765ea72b8111f36c522475d2daa0d22c957a9826437e97534bc4e9e"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "miniz_oxide"
|
name = "miniz_oxide"
|
||||||
version = "0.8.9"
|
version = "0.8.9"
|
||||||
@@ -2388,6 +2473,18 @@ dependencies = [
|
|||||||
"objc2-core-foundation",
|
"objc2-core-foundation",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "objc2-osa-kit"
|
||||||
|
version = "0.3.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "f112d1746737b0da274ef79a23aac283376f335f4095a083a267a082f21db0c0"
|
||||||
|
dependencies = [
|
||||||
|
"bitflags 2.13.1",
|
||||||
|
"objc2",
|
||||||
|
"objc2-app-kit",
|
||||||
|
"objc2-foundation",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "objc2-quartz-core"
|
name = "objc2-quartz-core"
|
||||||
version = "0.3.2"
|
version = "0.3.2"
|
||||||
@@ -2451,6 +2548,17 @@ version = "1.21.4"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "9f7c3e4beb33f85d45ae3e3a1792185706c8e16d043238c593331cc7cd313b50"
|
checksum = "9f7c3e4beb33f85d45ae3e3a1792185706c8e16d043238c593331cc7cd313b50"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "open"
|
||||||
|
version = "5.4.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "a0b3d059e795d52b8a72fef45658620edd4d9c359b338564aa14391ffa511ed5"
|
||||||
|
dependencies = [
|
||||||
|
"dunce",
|
||||||
|
"is-wsl",
|
||||||
|
"libc",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "openssl"
|
name = "openssl"
|
||||||
version = "0.10.81"
|
version = "0.10.81"
|
||||||
@@ -2510,6 +2618,20 @@ dependencies = [
|
|||||||
"pin-project-lite",
|
"pin-project-lite",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "osakit"
|
||||||
|
version = "0.3.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "732c71caeaa72c065bb69d7ea08717bd3f4863a4f451402fc9513e29dbd5261b"
|
||||||
|
dependencies = [
|
||||||
|
"objc2",
|
||||||
|
"objc2-foundation",
|
||||||
|
"objc2-osa-kit",
|
||||||
|
"serde",
|
||||||
|
"serde_json",
|
||||||
|
"thiserror 2.0.18",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "pango"
|
name = "pango"
|
||||||
version = "0.18.3"
|
version = "0.18.3"
|
||||||
@@ -3083,15 +3205,20 @@ dependencies = [
|
|||||||
"http-body",
|
"http-body",
|
||||||
"http-body-util",
|
"http-body-util",
|
||||||
"hyper",
|
"hyper",
|
||||||
|
"hyper-rustls",
|
||||||
"hyper-util",
|
"hyper-util",
|
||||||
"js-sys",
|
"js-sys",
|
||||||
"log",
|
"log",
|
||||||
"percent-encoding",
|
"percent-encoding",
|
||||||
"pin-project-lite",
|
"pin-project-lite",
|
||||||
|
"rustls",
|
||||||
|
"rustls-pki-types",
|
||||||
|
"rustls-platform-verifier",
|
||||||
"serde",
|
"serde",
|
||||||
"serde_json",
|
"serde_json",
|
||||||
"sync_wrapper",
|
"sync_wrapper",
|
||||||
"tokio",
|
"tokio",
|
||||||
|
"tokio-rustls",
|
||||||
"tokio-util",
|
"tokio-util",
|
||||||
"tower",
|
"tower",
|
||||||
"tower-http",
|
"tower-http",
|
||||||
@@ -3159,6 +3286,18 @@ dependencies = [
|
|||||||
"zeroize",
|
"zeroize",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "rustls-native-certs"
|
||||||
|
version = "0.8.4"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "dab5152771c58876a2146916e53e35057e1a4dfa2b9df0f0305b07f611fdea4d"
|
||||||
|
dependencies = [
|
||||||
|
"openssl-probe",
|
||||||
|
"rustls-pki-types",
|
||||||
|
"schannel",
|
||||||
|
"security-framework",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "rustls-pki-types"
|
name = "rustls-pki-types"
|
||||||
version = "1.15.0"
|
version = "1.15.0"
|
||||||
@@ -3169,6 +3308,33 @@ dependencies = [
|
|||||||
"zeroize",
|
"zeroize",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "rustls-platform-verifier"
|
||||||
|
version = "0.7.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "26d1e2536ce4f35f4846aa13bff16bd0ff40157cdb14cc056c7b14ba41233ba0"
|
||||||
|
dependencies = [
|
||||||
|
"core-foundation 0.10.1",
|
||||||
|
"core-foundation-sys",
|
||||||
|
"jni 0.22.4",
|
||||||
|
"log",
|
||||||
|
"once_cell",
|
||||||
|
"rustls",
|
||||||
|
"rustls-native-certs",
|
||||||
|
"rustls-platform-verifier-android",
|
||||||
|
"rustls-webpki",
|
||||||
|
"security-framework",
|
||||||
|
"security-framework-sys",
|
||||||
|
"webpki-root-certs",
|
||||||
|
"windows-sys 0.61.2",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "rustls-platform-verifier-android"
|
||||||
|
version = "0.1.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "f87165f0995f63a9fbeea62b64d10b4d9d8e78ec6d7d51fb2125fda7bb36788f"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "rustls-webpki"
|
name = "rustls-webpki"
|
||||||
version = "0.103.13"
|
version = "0.103.13"
|
||||||
@@ -3522,6 +3688,22 @@ version = "0.3.10"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "3a219298ac11a56ea9a6d2120044824d6f01aeb034955e7af7bc16858527deea"
|
checksum = "3a219298ac11a56ea9a6d2120044824d6f01aeb034955e7af7bc16858527deea"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "simd_cesu8"
|
||||||
|
version = "1.2.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "11031e251abf8611c80f460e19dbdeb54a66db918e49c65a7065b46ac7aec520"
|
||||||
|
dependencies = [
|
||||||
|
"rustc_version",
|
||||||
|
"simdutf8",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "simdutf8"
|
||||||
|
version = "0.1.5"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "e3a9fe34e3e7a50316060351f37187a3f546bce95496156754b601a5fa71b76e"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "siphasher"
|
name = "siphasher"
|
||||||
version = "1.0.3"
|
version = "1.0.3"
|
||||||
@@ -3683,6 +3865,8 @@ dependencies = [
|
|||||||
"tauri",
|
"tauri",
|
||||||
"tauri-build",
|
"tauri-build",
|
||||||
"tauri-plugin-notification",
|
"tauri-plugin-notification",
|
||||||
|
"tauri-plugin-opener",
|
||||||
|
"tauri-plugin-updater",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -3757,7 +3941,7 @@ dependencies = [
|
|||||||
"gdkwayland-sys",
|
"gdkwayland-sys",
|
||||||
"gdkx11-sys",
|
"gdkx11-sys",
|
||||||
"gtk",
|
"gtk",
|
||||||
"jni",
|
"jni 0.21.1",
|
||||||
"libc",
|
"libc",
|
||||||
"log",
|
"log",
|
||||||
"ndk",
|
"ndk",
|
||||||
@@ -3790,6 +3974,17 @@ dependencies = [
|
|||||||
"syn 2.0.119",
|
"syn 2.0.119",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "tar"
|
||||||
|
version = "0.4.46"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "3f6221d9a6003c78398e3b239969f352578258df48c8eb051caadae0015bc840"
|
||||||
|
dependencies = [
|
||||||
|
"filetime",
|
||||||
|
"libc",
|
||||||
|
"xattr",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "target-lexicon"
|
name = "target-lexicon"
|
||||||
version = "0.12.16"
|
version = "0.12.16"
|
||||||
@@ -3813,7 +4008,7 @@ dependencies = [
|
|||||||
"gtk",
|
"gtk",
|
||||||
"heck 0.5.0",
|
"heck 0.5.0",
|
||||||
"http",
|
"http",
|
||||||
"jni",
|
"jni 0.21.1",
|
||||||
"libc",
|
"libc",
|
||||||
"log",
|
"log",
|
||||||
"mime",
|
"mime",
|
||||||
@@ -3944,6 +4139,61 @@ dependencies = [
|
|||||||
"url",
|
"url",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "tauri-plugin-opener"
|
||||||
|
version = "2.5.4"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "17e1bea14edce6b793a04e2417e3fd924b9bc4faae83cdee7d714156cceeed29"
|
||||||
|
dependencies = [
|
||||||
|
"dunce",
|
||||||
|
"glob",
|
||||||
|
"objc2-app-kit",
|
||||||
|
"objc2-foundation",
|
||||||
|
"open",
|
||||||
|
"schemars 0.8.22",
|
||||||
|
"serde",
|
||||||
|
"serde_json",
|
||||||
|
"tauri",
|
||||||
|
"tauri-plugin",
|
||||||
|
"thiserror 2.0.18",
|
||||||
|
"url",
|
||||||
|
"windows",
|
||||||
|
"zbus",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "tauri-plugin-updater"
|
||||||
|
version = "2.10.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "806d9dac662c2e4594ff03c647a552f2c9bd544e7d0f683ec58f872f952ce4af"
|
||||||
|
dependencies = [
|
||||||
|
"base64 0.22.1",
|
||||||
|
"dirs",
|
||||||
|
"flate2",
|
||||||
|
"futures-util",
|
||||||
|
"http",
|
||||||
|
"infer",
|
||||||
|
"log",
|
||||||
|
"minisign-verify",
|
||||||
|
"osakit",
|
||||||
|
"percent-encoding",
|
||||||
|
"reqwest 0.13.4",
|
||||||
|
"rustls",
|
||||||
|
"semver",
|
||||||
|
"serde",
|
||||||
|
"serde_json",
|
||||||
|
"tar",
|
||||||
|
"tauri",
|
||||||
|
"tauri-plugin",
|
||||||
|
"tempfile",
|
||||||
|
"thiserror 2.0.18",
|
||||||
|
"time",
|
||||||
|
"tokio",
|
||||||
|
"url",
|
||||||
|
"windows-sys 0.60.2",
|
||||||
|
"zip",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "tauri-runtime"
|
name = "tauri-runtime"
|
||||||
version = "2.11.3"
|
version = "2.11.3"
|
||||||
@@ -3954,7 +4204,7 @@ dependencies = [
|
|||||||
"dpi",
|
"dpi",
|
||||||
"gtk",
|
"gtk",
|
||||||
"http",
|
"http",
|
||||||
"jni",
|
"jni 0.21.1",
|
||||||
"objc2",
|
"objc2",
|
||||||
"objc2-ui-kit",
|
"objc2-ui-kit",
|
||||||
"objc2-web-kit",
|
"objc2-web-kit",
|
||||||
@@ -3977,7 +4227,7 @@ checksum = "4e6fac707727b7a2f48e4ded90976324267371073edbb415ffb73bb0458d203f"
|
|||||||
dependencies = [
|
dependencies = [
|
||||||
"gtk",
|
"gtk",
|
||||||
"http",
|
"http",
|
||||||
"jni",
|
"jni 0.21.1",
|
||||||
"log",
|
"log",
|
||||||
"objc2",
|
"objc2",
|
||||||
"objc2-app-kit",
|
"objc2-app-kit",
|
||||||
@@ -4784,6 +5034,15 @@ dependencies = [
|
|||||||
"system-deps",
|
"system-deps",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "webpki-root-certs"
|
||||||
|
version = "1.0.8"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "0d46a5a140e6f7afeccd8eae97eff335163939eac8b929834875168b29b3d267"
|
||||||
|
dependencies = [
|
||||||
|
"rustls-pki-types",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "webpki-roots"
|
name = "webpki-roots"
|
||||||
version = "1.0.8"
|
version = "1.0.8"
|
||||||
@@ -5052,6 +5311,15 @@ dependencies = [
|
|||||||
"windows-targets 0.52.6",
|
"windows-targets 0.52.6",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows-sys"
|
||||||
|
version = "0.60.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "f2f500e4d28234f72040990ec9d39e3a6b950f9f22d3dba18416c35882612bcb"
|
||||||
|
dependencies = [
|
||||||
|
"windows-targets 0.53.5",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "windows-sys"
|
name = "windows-sys"
|
||||||
version = "0.61.2"
|
version = "0.61.2"
|
||||||
@@ -5085,13 +5353,30 @@ dependencies = [
|
|||||||
"windows_aarch64_gnullvm 0.52.6",
|
"windows_aarch64_gnullvm 0.52.6",
|
||||||
"windows_aarch64_msvc 0.52.6",
|
"windows_aarch64_msvc 0.52.6",
|
||||||
"windows_i686_gnu 0.52.6",
|
"windows_i686_gnu 0.52.6",
|
||||||
"windows_i686_gnullvm",
|
"windows_i686_gnullvm 0.52.6",
|
||||||
"windows_i686_msvc 0.52.6",
|
"windows_i686_msvc 0.52.6",
|
||||||
"windows_x86_64_gnu 0.52.6",
|
"windows_x86_64_gnu 0.52.6",
|
||||||
"windows_x86_64_gnullvm 0.52.6",
|
"windows_x86_64_gnullvm 0.52.6",
|
||||||
"windows_x86_64_msvc 0.52.6",
|
"windows_x86_64_msvc 0.52.6",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows-targets"
|
||||||
|
version = "0.53.5"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "4945f9f551b88e0d65f3db0bc25c33b8acea4d9e41163edf90dcd0b19f9069f3"
|
||||||
|
dependencies = [
|
||||||
|
"windows-link 0.2.1",
|
||||||
|
"windows_aarch64_gnullvm 0.53.1",
|
||||||
|
"windows_aarch64_msvc 0.53.1",
|
||||||
|
"windows_i686_gnu 0.53.1",
|
||||||
|
"windows_i686_gnullvm 0.53.1",
|
||||||
|
"windows_i686_msvc 0.53.1",
|
||||||
|
"windows_x86_64_gnu 0.53.1",
|
||||||
|
"windows_x86_64_gnullvm 0.53.1",
|
||||||
|
"windows_x86_64_msvc 0.53.1",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "windows-threading"
|
name = "windows-threading"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
@@ -5122,6 +5407,12 @@ version = "0.52.6"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3"
|
checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows_aarch64_gnullvm"
|
||||||
|
version = "0.53.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "a9d8416fa8b42f5c947f8482c43e7d89e73a173cead56d044f6a56104a6d1b53"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "windows_aarch64_msvc"
|
name = "windows_aarch64_msvc"
|
||||||
version = "0.42.2"
|
version = "0.42.2"
|
||||||
@@ -5134,6 +5425,12 @@ version = "0.52.6"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469"
|
checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows_aarch64_msvc"
|
||||||
|
version = "0.53.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "b9d782e804c2f632e395708e99a94275910eb9100b2114651e04744e9b125006"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "windows_i686_gnu"
|
name = "windows_i686_gnu"
|
||||||
version = "0.42.2"
|
version = "0.42.2"
|
||||||
@@ -5146,12 +5443,24 @@ version = "0.52.6"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b"
|
checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows_i686_gnu"
|
||||||
|
version = "0.53.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "960e6da069d81e09becb0ca57a65220ddff016ff2d6af6a223cf372a506593a3"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "windows_i686_gnullvm"
|
name = "windows_i686_gnullvm"
|
||||||
version = "0.52.6"
|
version = "0.52.6"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66"
|
checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows_i686_gnullvm"
|
||||||
|
version = "0.53.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "fa7359d10048f68ab8b09fa71c3daccfb0e9b559aed648a8f95469c27057180c"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "windows_i686_msvc"
|
name = "windows_i686_msvc"
|
||||||
version = "0.42.2"
|
version = "0.42.2"
|
||||||
@@ -5164,6 +5473,12 @@ version = "0.52.6"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66"
|
checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows_i686_msvc"
|
||||||
|
version = "0.53.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "1e7ac75179f18232fe9c285163565a57ef8d3c89254a30685b57d83a38d326c2"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "windows_x86_64_gnu"
|
name = "windows_x86_64_gnu"
|
||||||
version = "0.42.2"
|
version = "0.42.2"
|
||||||
@@ -5176,6 +5491,12 @@ version = "0.52.6"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78"
|
checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows_x86_64_gnu"
|
||||||
|
version = "0.53.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "9c3842cdd74a865a8066ab39c8a7a473c0778a3f29370b5fd6b4b9aa7df4a499"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "windows_x86_64_gnullvm"
|
name = "windows_x86_64_gnullvm"
|
||||||
version = "0.42.2"
|
version = "0.42.2"
|
||||||
@@ -5188,6 +5509,12 @@ version = "0.52.6"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d"
|
checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows_x86_64_gnullvm"
|
||||||
|
version = "0.53.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "0ffa179e2d07eee8ad8f57493436566c7cc30ac536a3379fdf008f47f6bb7ae1"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "windows_x86_64_msvc"
|
name = "windows_x86_64_msvc"
|
||||||
version = "0.42.2"
|
version = "0.42.2"
|
||||||
@@ -5200,6 +5527,12 @@ version = "0.52.6"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec"
|
checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows_x86_64_msvc"
|
||||||
|
version = "0.53.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "d6bbff5f0aada427a1e5a6da5f1f98158182f26556f345ac9e04d36d0ebed650"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "winnow"
|
name = "winnow"
|
||||||
version = "0.5.40"
|
version = "0.5.40"
|
||||||
@@ -5264,7 +5597,7 @@ dependencies = [
|
|||||||
"gtk",
|
"gtk",
|
||||||
"http",
|
"http",
|
||||||
"javascriptcore-rs",
|
"javascriptcore-rs",
|
||||||
"jni",
|
"jni 0.21.1",
|
||||||
"libc",
|
"libc",
|
||||||
"ndk",
|
"ndk",
|
||||||
"objc2",
|
"objc2",
|
||||||
@@ -5311,6 +5644,16 @@ dependencies = [
|
|||||||
"pkg-config",
|
"pkg-config",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "xattr"
|
||||||
|
version = "1.6.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "32e45ad4206f6d2479085147f02bc2ef834ac85886624a23575ae137c8aa8156"
|
||||||
|
dependencies = [
|
||||||
|
"libc",
|
||||||
|
"rustix",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "yoke"
|
name = "yoke"
|
||||||
version = "0.8.3"
|
version = "0.8.3"
|
||||||
@@ -5475,6 +5818,18 @@ dependencies = [
|
|||||||
"syn 2.0.119",
|
"syn 2.0.119",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "zip"
|
||||||
|
version = "4.6.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "caa8cd6af31c3b31c6631b8f483848b91589021b28fffe50adada48d4f4d2ed1"
|
||||||
|
dependencies = [
|
||||||
|
"arbitrary",
|
||||||
|
"crc32fast",
|
||||||
|
"indexmap 2.14.0",
|
||||||
|
"memchr",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "zmij"
|
name = "zmij"
|
||||||
version = "1.0.23"
|
version = "1.0.23"
|
||||||
|
|||||||
@@ -23,6 +23,8 @@ serde = { version = "1", features = ["derive"] }
|
|||||||
serde_json = "1"
|
serde_json = "1"
|
||||||
reqwest = { version = "0.12", features = ["cookies", "json", "rustls-tls"] }
|
reqwest = { version = "0.12", features = ["cookies", "json", "rustls-tls"] }
|
||||||
tauri-plugin-notification = "2"
|
tauri-plugin-notification = "2"
|
||||||
|
tauri-plugin-opener = "2"
|
||||||
|
tauri-plugin-updater = "2"
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
http = "1"
|
http = "1"
|
||||||
|
|||||||
@@ -8,6 +8,8 @@ fn main() {
|
|||||||
"has_web_session",
|
"has_web_session",
|
||||||
"open_web_app",
|
"open_web_app",
|
||||||
"open_login_window",
|
"open_login_window",
|
||||||
|
"open_external_url",
|
||||||
|
"reveal_window",
|
||||||
]),
|
]),
|
||||||
))
|
))
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|||||||
@@ -0,0 +1,11 @@
|
|||||||
|
# Automatically generated - DO NOT EDIT!
|
||||||
|
|
||||||
|
[[permission]]
|
||||||
|
identifier = "allow-open-external-url"
|
||||||
|
description = "Enables the open_external_url command without any pre-configured scope."
|
||||||
|
commands.allow = ["open_external_url"]
|
||||||
|
|
||||||
|
[[permission]]
|
||||||
|
identifier = "deny-open-external-url"
|
||||||
|
description = "Denies the open_external_url command without any pre-configured scope."
|
||||||
|
commands.deny = ["open_external_url"]
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
# Automatically generated - DO NOT EDIT!
|
||||||
|
|
||||||
|
[[permission]]
|
||||||
|
identifier = "allow-reveal-window"
|
||||||
|
description = "Enables the reveal_window command without any pre-configured scope."
|
||||||
|
commands.allow = ["reveal_window"]
|
||||||
|
|
||||||
|
[[permission]]
|
||||||
|
identifier = "deny-reveal-window"
|
||||||
|
description = "Denies the reveal_window command without any pre-configured scope."
|
||||||
|
commands.deny = ["reveal_window"]
|
||||||
@@ -9,4 +9,6 @@ commands.allow = [
|
|||||||
"has_web_session",
|
"has_web_session",
|
||||||
"open_web_app",
|
"open_web_app",
|
||||||
"open_login_window",
|
"open_login_window",
|
||||||
|
"open_external_url",
|
||||||
|
"reveal_window",
|
||||||
]
|
]
|
||||||
|
|||||||
+195
-5
@@ -6,6 +6,8 @@ use std::sync::Mutex;
|
|||||||
use std::time::Duration;
|
use std::time::Duration;
|
||||||
use tauri::webview::{Cookie, PageLoadEvent};
|
use tauri::webview::{Cookie, PageLoadEvent};
|
||||||
use tauri::{Manager, State, Webview, WebviewUrl, WebviewWindow, WebviewWindowBuilder};
|
use tauri::{Manager, State, Webview, WebviewUrl, WebviewWindow, WebviewWindowBuilder};
|
||||||
|
use tauri_plugin_opener::OpenerExt;
|
||||||
|
use tauri_plugin_updater::UpdaterExt;
|
||||||
|
|
||||||
struct AppState {
|
struct AppState {
|
||||||
client: Client,
|
client: Client,
|
||||||
@@ -56,6 +58,85 @@ struct ErrorPayload {
|
|||||||
error: Option<String>,
|
error: Option<String>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const UPDATER_PUBLIC_KEY: &str = "dW50cnVzdGVkIGNvbW1lbnQ6IG1pbmlzaWduIHB1YmxpYyBrZXk6IEYzOEZCMTg0QzJERkUwNTcKUldSWDROL0NoTEdQODlCOW10cXAvYzNTNitFQkxUd3F2T3UybUV3REJYT1Z6OVczazFmamxhWlIK";
|
||||||
|
const LATEST_RELEASE_API: &str =
|
||||||
|
"https://githop.xyz/api/v1/repos/GnosysLabs/Synapsis-Desktop/releases/latest";
|
||||||
|
|
||||||
|
#[derive(Debug, Deserialize)]
|
||||||
|
struct ReleaseAsset {
|
||||||
|
name: String,
|
||||||
|
browser_download_url: Url,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Deserialize)]
|
||||||
|
struct ReleaseInfo {
|
||||||
|
assets: Vec<ReleaseAsset>,
|
||||||
|
}
|
||||||
|
|
||||||
|
fn manifest_url(release: ReleaseInfo) -> Option<Url> {
|
||||||
|
release
|
||||||
|
.assets
|
||||||
|
.into_iter()
|
||||||
|
.find(|asset| asset.name == "latest.json")
|
||||||
|
.map(|asset| asset.browser_download_url)
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn latest_manifest_url(client: &Client) -> Result<Option<Url>, String> {
|
||||||
|
let response = client
|
||||||
|
.get(LATEST_RELEASE_API)
|
||||||
|
.header("Accept", "application/json")
|
||||||
|
.send()
|
||||||
|
.await
|
||||||
|
.map_err(|error| format!("Couldn’t reach the Synapsis release feed: {error}"))?;
|
||||||
|
if response.status() == StatusCode::NOT_FOUND {
|
||||||
|
return Ok(None);
|
||||||
|
}
|
||||||
|
let release = decode_response::<ReleaseInfo>(response).await?;
|
||||||
|
Ok(manifest_url(release))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn start_auto_updater(app: tauri::AppHandle, client: Client) {
|
||||||
|
if cfg!(debug_assertions) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
tauri::async_runtime::spawn(async move {
|
||||||
|
let endpoint = match latest_manifest_url(&client).await {
|
||||||
|
Ok(Some(endpoint)) => endpoint,
|
||||||
|
Ok(None) => return,
|
||||||
|
Err(error) => {
|
||||||
|
eprintln!("Couldn’t discover the latest Synapsis release: {error}");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
let updater = match app
|
||||||
|
.updater_builder()
|
||||||
|
.pubkey(UPDATER_PUBLIC_KEY)
|
||||||
|
.endpoints(vec![endpoint])
|
||||||
|
.and_then(|builder| builder.build())
|
||||||
|
{
|
||||||
|
Ok(updater) => updater,
|
||||||
|
Err(error) => {
|
||||||
|
eprintln!("Couldn’t initialize Synapsis auto-updates: {error}");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
match updater.check().await {
|
||||||
|
Ok(Some(update)) => {
|
||||||
|
let version = update.version.clone();
|
||||||
|
if let Err(error) = update.download_and_install(|_, _| {}, || {}).await {
|
||||||
|
eprintln!("Couldn’t install Synapsis {version}: {error}");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
app.restart();
|
||||||
|
}
|
||||||
|
Ok(None) => {}
|
||||||
|
Err(error) => eprintln!("Couldn’t check for a Synapsis update: {error}"),
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Serialize)]
|
#[derive(Serialize)]
|
||||||
#[serde(rename_all = "camelCase")]
|
#[serde(rename_all = "camelCase")]
|
||||||
struct RegistrationRequest<'a> {
|
struct RegistrationRequest<'a> {
|
||||||
@@ -304,7 +385,11 @@ fn remote_node_capability(origin: &str, window_label: &str, sequence: u64) -> St
|
|||||||
"remote": {
|
"remote": {
|
||||||
"urls": [format!("{origin}/*")]
|
"urls": [format!("{origin}/*")]
|
||||||
},
|
},
|
||||||
"permissions": ["notification:default", "allow-open-login-window"]
|
"permissions": [
|
||||||
|
"notification:default",
|
||||||
|
"allow-open-login-window",
|
||||||
|
"allow-open-external-url"
|
||||||
|
]
|
||||||
})
|
})
|
||||||
.to_string()
|
.to_string()
|
||||||
}
|
}
|
||||||
@@ -377,6 +462,48 @@ async fn install_new_window_button(client: Client, webview: Webview, page_url: U
|
|||||||
let _ = webview.eval(new_window_button_script(accent_color.as_deref()));
|
let _ = webview.eval(new_window_button_script(accent_color.as_deref()));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn external_link_script() -> &'static str {
|
||||||
|
r#"
|
||||||
|
(() => {
|
||||||
|
if (window.__SYNAPSIS_EXTERNAL_LINKS_INSTALLED__) return;
|
||||||
|
window.__SYNAPSIS_EXTERNAL_LINKS_INSTALLED__ = true;
|
||||||
|
|
||||||
|
document.addEventListener("click", (event) => {
|
||||||
|
if (event.defaultPrevented || event.button !== 0) return;
|
||||||
|
const link = event.target instanceof Element ? event.target.closest("a[href]") : null;
|
||||||
|
if (!link || link.hasAttribute("download")) return;
|
||||||
|
|
||||||
|
let destination;
|
||||||
|
try {
|
||||||
|
destination = new URL(link.href, window.location.href);
|
||||||
|
} catch {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!/^https?:$/.test(destination.protocol) || destination.origin === window.location.origin) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
event.preventDefault();
|
||||||
|
event.stopImmediatePropagation();
|
||||||
|
window.__TAURI_INTERNALS__.invoke("open_external_url", { url: destination.href })
|
||||||
|
.catch((error) => console.error("Could not open external link", error));
|
||||||
|
}, true);
|
||||||
|
})();
|
||||||
|
"#
|
||||||
|
}
|
||||||
|
|
||||||
|
#[tauri::command]
|
||||||
|
fn open_external_url(app: tauri::AppHandle, url: String) -> Result<(), String> {
|
||||||
|
let destination = Url::parse(&url).map_err(|_| "This link is not a valid URL.".to_string())?;
|
||||||
|
if !matches!(destination.scheme(), "http" | "https") {
|
||||||
|
return Err("Only web links can be opened externally.".into());
|
||||||
|
}
|
||||||
|
app.opener()
|
||||||
|
.open_url(destination.as_str(), None::<&str>)
|
||||||
|
.map_err(|error| format!("Couldn’t open this link in the default browser: {error}"))
|
||||||
|
}
|
||||||
|
|
||||||
#[tauri::command]
|
#[tauri::command]
|
||||||
async fn open_login_window(
|
async fn open_login_window(
|
||||||
app: tauri::AppHandle,
|
app: tauri::AppHandle,
|
||||||
@@ -395,6 +522,7 @@ async fn open_login_window(
|
|||||||
.inner_size(1280.0, 820.0)
|
.inner_size(1280.0, 820.0)
|
||||||
.min_inner_size(720.0, 560.0)
|
.min_inner_size(720.0, 560.0)
|
||||||
.center()
|
.center()
|
||||||
|
.visible(false)
|
||||||
.hidden_title(true)
|
.hidden_title(true)
|
||||||
.title_bar_style(tauri::TitleBarStyle::Visible)
|
.title_bar_style(tauri::TitleBarStyle::Visible)
|
||||||
.theme(Some(tauri::Theme::Dark))
|
.theme(Some(tauri::Theme::Dark))
|
||||||
@@ -404,6 +532,19 @@ async fn open_login_window(
|
|||||||
.map_err(|error| format!("Couldn’t open another Synapsis window: {error}"))
|
.map_err(|error| format!("Couldn’t open another Synapsis window: {error}"))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[tauri::command]
|
||||||
|
fn reveal_window(webview: WebviewWindow) -> Result<(), String> {
|
||||||
|
if !webview.label().starts_with("node-") {
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
|
webview
|
||||||
|
.show()
|
||||||
|
.map_err(|error| format!("Couldn’t reveal the Synapsis window: {error}"))?;
|
||||||
|
webview
|
||||||
|
.set_focus()
|
||||||
|
.map_err(|error| format!("Couldn’t focus the Synapsis window: {error}"))
|
||||||
|
}
|
||||||
|
|
||||||
#[tauri::command]
|
#[tauri::command]
|
||||||
async fn logout(state: State<'_, AppState>, node_url: String) -> Result<(), String> {
|
async fn logout(state: State<'_, AppState>, node_url: String) -> Result<(), String> {
|
||||||
let base = normalize_node(&node_url)?;
|
let base = normalize_node(&node_url)?;
|
||||||
@@ -432,19 +573,34 @@ pub fn run() {
|
|||||||
|
|
||||||
tauri::Builder::default()
|
tauri::Builder::default()
|
||||||
.plugin(tauri_plugin_notification::init())
|
.plugin(tauri_plugin_notification::init())
|
||||||
|
.plugin(tauri_plugin_opener::init())
|
||||||
|
.plugin(
|
||||||
|
tauri_plugin_updater::Builder::new()
|
||||||
|
.pubkey(UPDATER_PUBLIC_KEY)
|
||||||
|
.build(),
|
||||||
|
)
|
||||||
.manage(AppState {
|
.manage(AppState {
|
||||||
client,
|
client,
|
||||||
notification_capability_sequence: AtomicU64::new(0),
|
notification_capability_sequence: AtomicU64::new(0),
|
||||||
window_sequence: AtomicU64::new(1),
|
window_sequence: AtomicU64::new(1),
|
||||||
entry_urls: Mutex::new(HashMap::new()),
|
entry_urls: Mutex::new(HashMap::new()),
|
||||||
})
|
})
|
||||||
|
.setup(|app| {
|
||||||
|
let client = app.state::<AppState>().client.clone();
|
||||||
|
start_auto_updater(app.handle().clone(), client);
|
||||||
|
Ok(())
|
||||||
|
})
|
||||||
.on_page_load(|webview, payload| {
|
.on_page_load(|webview, payload| {
|
||||||
if payload.event() != PageLoadEvent::Finished
|
if payload.event() != PageLoadEvent::Finished {
|
||||||
|| !matches!(payload.url().scheme(), "http" | "https")
|
|
||||||
{
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if !matches!(payload.url().scheme(), "http" | "https") {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
let _ = webview.eval(external_link_script());
|
||||||
|
|
||||||
let Ok(cookies) = webview.cookies_for_url(payload.url().clone()) else {
|
let Ok(cookies) = webview.cookies_for_url(payload.url().clone()) else {
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
@@ -493,7 +649,9 @@ pub fn run() {
|
|||||||
logout,
|
logout,
|
||||||
has_web_session,
|
has_web_session,
|
||||||
open_web_app,
|
open_web_app,
|
||||||
open_login_window
|
open_login_window,
|
||||||
|
open_external_url,
|
||||||
|
reveal_window
|
||||||
])
|
])
|
||||||
.run(tauri::generate_context!())
|
.run(tauri::generate_context!())
|
||||||
.expect("error while running Synapsis");
|
.expect("error while running Synapsis");
|
||||||
@@ -559,6 +717,7 @@ mod tests {
|
|||||||
assert_eq!(value["remote"]["urls"][0], "https://example.com/*");
|
assert_eq!(value["remote"]["urls"][0], "https://example.com/*");
|
||||||
assert_eq!(value["permissions"][0], "notification:default");
|
assert_eq!(value["permissions"][0], "notification:default");
|
||||||
assert_eq!(value["permissions"][1], "allow-open-login-window");
|
assert_eq!(value["permissions"][1], "allow-open-login-window");
|
||||||
|
assert_eq!(value["permissions"][2], "allow-open-external-url");
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
@@ -571,6 +730,15 @@ mod tests {
|
|||||||
assert!(script.contains("open_login_window"));
|
assert!(script.contains("open_login_window"));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn intercepts_external_links_without_hijacking_same_origin_navigation() {
|
||||||
|
let script = external_link_script();
|
||||||
|
|
||||||
|
assert!(script.contains("destination.origin === window.location.origin"));
|
||||||
|
assert!(script.contains("open_external_url"));
|
||||||
|
assert!(script.contains("event.preventDefault()"));
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn returns_to_entry_only_for_logged_out_explore() {
|
fn returns_to_entry_only_for_logged_out_explore() {
|
||||||
let explore = Url::parse("https://example.com/explore").unwrap();
|
let explore = Url::parse("https://example.com/explore").unwrap();
|
||||||
@@ -581,4 +749,26 @@ mod tests {
|
|||||||
assert!(!is_logged_out_explore(&timeline, &[]));
|
assert!(!is_logged_out_explore(&timeline, &[]));
|
||||||
assert!(!is_logged_out_explore(&explore, &[active_cookie]));
|
assert!(!is_logged_out_explore(&explore, &[active_cookie]));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn selects_latest_json_from_githop_release_assets() {
|
||||||
|
let release = ReleaseInfo {
|
||||||
|
assets: vec![
|
||||||
|
ReleaseAsset {
|
||||||
|
name: "Synapsis.app.tar.gz".into(),
|
||||||
|
browser_download_url: Url::parse("https://example.com/Synapsis.app.tar.gz")
|
||||||
|
.unwrap(),
|
||||||
|
},
|
||||||
|
ReleaseAsset {
|
||||||
|
name: "latest.json".into(),
|
||||||
|
browser_download_url: Url::parse("https://example.com/latest.json").unwrap(),
|
||||||
|
},
|
||||||
|
],
|
||||||
|
};
|
||||||
|
|
||||||
|
assert_eq!(
|
||||||
|
manifest_url(release).unwrap().as_str(),
|
||||||
|
"https://example.com/latest.json"
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -38,5 +38,11 @@
|
|||||||
"icons/icon.icns",
|
"icons/icon.icns",
|
||||||
"icons/icon.ico"
|
"icons/icon.ico"
|
||||||
]
|
]
|
||||||
|
},
|
||||||
|
"plugins": {
|
||||||
|
"updater": {
|
||||||
|
"pubkey": "dW50cnVzdGVkIGNvbW1lbnQ6IG1pbmlzaWduIHB1YmxpYyBrZXk6IEYzOEZCMTg0QzJERkUwNTcKUldSWDROL0NoTEdQODlCOW10cXAvYzNTNitFQkxUd3F2T3UybUV3REJYT1Z6OVczazFmamxhWlIK",
|
||||||
|
"endpoints": []
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,5 @@
|
|||||||
|
{
|
||||||
|
"bundle": {
|
||||||
|
"createUpdaterArtifacts": true
|
||||||
|
}
|
||||||
|
}
|
||||||
+22
@@ -264,6 +264,10 @@ function SignalField() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function App() {
|
function App() {
|
||||||
|
const isSecondaryWindow = useRef(
|
||||||
|
Boolean(window.__SYNAPSIS_FRESH_LOGIN__) ||
|
||||||
|
new URLSearchParams(window.location.search).has("newWindow"),
|
||||||
|
);
|
||||||
const [screen, setScreen] = useState<Screen>("node");
|
const [screen, setScreen] = useState<Screen>("node");
|
||||||
const [nodeInput, setNodeInput] = useState(DEFAULT_NODE);
|
const [nodeInput, setNodeInput] = useState(DEFAULT_NODE);
|
||||||
const [nodeUrl, setNodeUrl] = useState("");
|
const [nodeUrl, setNodeUrl] = useState("");
|
||||||
@@ -325,6 +329,24 @@ function App() {
|
|||||||
void restore();
|
void restore();
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (bootstrapping || !isSecondaryWindow.current || !isTauri()) return;
|
||||||
|
let cancelled = false;
|
||||||
|
let secondFrame = 0;
|
||||||
|
const firstFrame = requestAnimationFrame(() => {
|
||||||
|
secondFrame = requestAnimationFrame(() => {
|
||||||
|
if (!cancelled) {
|
||||||
|
void invoke("reveal_window");
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
return () => {
|
||||||
|
cancelled = true;
|
||||||
|
cancelAnimationFrame(firstFrame);
|
||||||
|
cancelAnimationFrame(secondFrame);
|
||||||
|
};
|
||||||
|
}, [bootstrapping]);
|
||||||
|
|
||||||
async function connect(event: FormEvent) {
|
async function connect(event: FormEvent) {
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
if (!isTauri()) {
|
if (!isTauri()) {
|
||||||
|
|||||||
Reference in New Issue
Block a user