Merge pull request #1519 from safing/feature/tauri-beta-update
Feature Tauri beta update
2
.github/workflows/angular.yml
vendored
|
@ -56,4 +56,4 @@ jobs:
|
||||||
password: ${{ secrets.GITHUB_TOKEN }}
|
password: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
|
||||||
- name: Build angular projects
|
- name: Build angular projects
|
||||||
run: earthly --ci --remote-cache=ghcr.io/safing/build-cache --push +angular-release
|
run: earthly --ci --remote-cache=ghcr.io/safing/build-cache --push +angular-ci
|
||||||
|
|
2
.github/workflows/kext.yml
vendored
|
@ -33,4 +33,4 @@ jobs:
|
||||||
password: ${{ secrets.GITHUB_TOKEN }}
|
password: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
|
||||||
- name: Build Kernel Extension
|
- name: Build Kernel Extension
|
||||||
run: earthly --ci --remote-cache=ghcr.io/safing/build-cache --push +kext-build
|
run: earthly --ci --remote-cache=ghcr.io/safing/build-cache --push +kext-ci
|
||||||
|
|
2
.github/workflows/tauri.yml
vendored
|
@ -33,4 +33,4 @@ jobs:
|
||||||
password: ${{ secrets.GITHUB_TOKEN }}
|
password: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
|
||||||
- name: Build tauri project
|
- name: Build tauri project
|
||||||
run: earthly --ci --remote-cache=ghcr.io/safing/build-cache --push +tauri-release
|
run: earthly --ci --remote-cache=ghcr.io/safing/build-cache --push +tauri-ci
|
||||||
|
|
386
Earthfile
|
@ -12,24 +12,90 @@ ARG --global work_image = "alpine"
|
||||||
|
|
||||||
ARG --global outputDir = "./dist"
|
ARG --global outputDir = "./dist"
|
||||||
|
|
||||||
|
# Architectures:
|
||||||
# The list of rust targets we support. They will be automatically converted
|
# The list of rust targets we support. They will be automatically converted
|
||||||
# to GOOS, GOARCH and GOARM when building go binaries. See the +RUST_TO_GO_ARCH_STRING
|
# to GOOS, GOARCH and GOARM when building go binaries. See the +RUST_TO_GO_ARCH_STRING
|
||||||
# helper method at the bottom of the file.
|
# helper method at the bottom of the file.
|
||||||
|
#
|
||||||
ARG --global architectures = "x86_64-unknown-linux-gnu" \
|
# Linux:
|
||||||
"aarch64-unknown-linux-gnu" \
|
# x86_64-unknown-linux-gnu
|
||||||
"x86_64-pc-windows-gnu"
|
# aarch64-unknown-linux-gnu
|
||||||
# TODO: Compile errors here:
|
# armv7-unknown-linux-gnueabihf
|
||||||
# "aarch64-pc-windows-gnu" \
|
# arm-unknown-linux-gnueabi
|
||||||
# "x86_64-apple-darwin" \
|
#
|
||||||
# "aarch64-apple-darwin"
|
# Windows:
|
||||||
# "armv7-unknown-linux-gnueabihf" \
|
# x86_64-pc-windows-gnu
|
||||||
# "arm-unknown-linux-gnueabi"
|
# aarch64-pc-windows-gnu
|
||||||
|
#
|
||||||
|
# Mac:
|
||||||
|
# x86_64-apple-darwin
|
||||||
|
# aarch64-apple-darwin
|
||||||
|
|
||||||
# Import the earthly rust lib since it already provides some useful
|
# Import the earthly rust lib since it already provides some useful
|
||||||
# build-targets and methods to initialize the rust toolchain.
|
# build-targets and methods to initialize the rust toolchain.
|
||||||
IMPORT github.com/earthly/lib/rust:3.0.2 AS rust
|
IMPORT github.com/earthly/lib/rust:3.0.2 AS rust
|
||||||
|
|
||||||
|
build:
|
||||||
|
# Build all Golang binaries:
|
||||||
|
# ./dist/linux_amd64/portmaster-core
|
||||||
|
# ./dist/linux_amd64/portmaster-start
|
||||||
|
# ./dist/linux_arm64/portmaster-core
|
||||||
|
# ./dist/linux_arm64/portmaster-start
|
||||||
|
# ./dist/windows_amd64/portmaster-core.exe
|
||||||
|
# ./dist/windows_amd64/portmaster-start.exe
|
||||||
|
# ./dist/windows_arm64/portmaster-core.exe
|
||||||
|
# ./dist/windows_arm64/portmaster-start.exe
|
||||||
|
BUILD +go-build --GOOS="linux" --GOARCH="amd64"
|
||||||
|
BUILD +go-build --GOOS="linux" --GOARCH="arm64"
|
||||||
|
BUILD +go-build --GOOS="windows" --GOARCH="amd64"
|
||||||
|
BUILD +go-build --GOOS="windows" --GOARCH="arm64"
|
||||||
|
|
||||||
|
# Build the Angular UI:
|
||||||
|
# ./dist/all/portmaster-ui.zip
|
||||||
|
BUILD +angular-release
|
||||||
|
|
||||||
|
# Build Tauri app binaries:
|
||||||
|
# ./dist/linux_amd64/portmaster-app
|
||||||
|
# ./dist/linux_amd64/Portmaster-0.1.0-1.x86_64.rpm
|
||||||
|
# ./dist/linux_amd64/Portmaster_0.1.0_amd64.deb
|
||||||
|
BUILD +tauri-build --target="x86_64-unknown-linux-gnu"
|
||||||
|
# TODO:
|
||||||
|
# BUILD +tauri-build --target="x86_64-pc-windows-gnu"
|
||||||
|
|
||||||
|
# Bild Tauri release bundle for Windows:
|
||||||
|
# ./dist/windows_amd64/portmaster-app_vX-X-X.zip
|
||||||
|
BUILD +tauri-windows-release-bundle
|
||||||
|
|
||||||
|
# Build UI assets:
|
||||||
|
# ./dist/all/assets.zip
|
||||||
|
BUILD +assets
|
||||||
|
|
||||||
|
go-ci:
|
||||||
|
BUILD +go-build --GOOS="linux" --GOARCH="amd64"
|
||||||
|
BUILD +go-build --GOOS="linux" --GOARCH="arm64"
|
||||||
|
BUILD +go-build --GOOS="windows" --GOARCH="amd64"
|
||||||
|
BUILD +go-build --GOOS="windows" --GOARCH="arm64"
|
||||||
|
BUILD +go-test
|
||||||
|
|
||||||
|
angular-ci:
|
||||||
|
BUILD +angular-release
|
||||||
|
|
||||||
|
tauri-ci:
|
||||||
|
BUILD +tauri-build --target="x86_64-unknown-linux-gnu"
|
||||||
|
BUILD +tauri-windows-release-bundle
|
||||||
|
|
||||||
|
kext-ci:
|
||||||
|
BUILD +kext-build
|
||||||
|
|
||||||
|
release:
|
||||||
|
LOCALLY
|
||||||
|
|
||||||
|
IF ! git diff --quiet
|
||||||
|
RUN echo -e "\033[1;31m Refusing to release a dirty git repository. Please commit your local changes first! \033[0m" ; exit 1
|
||||||
|
END
|
||||||
|
|
||||||
|
BUILD +build
|
||||||
|
|
||||||
go-deps:
|
go-deps:
|
||||||
FROM ${go_builder_image}
|
FROM ${go_builder_image}
|
||||||
WORKDIR /go-workdir
|
WORKDIR /go-workdir
|
||||||
|
@ -111,7 +177,7 @@ go-build:
|
||||||
ARG GOOS=linux
|
ARG GOOS=linux
|
||||||
ARG GOARCH=amd64
|
ARG GOARCH=amd64
|
||||||
ARG GOARM
|
ARG GOARM
|
||||||
ARG CMDS=portmaster-start portmaster-core hub notifier
|
ARG CMDS=portmaster-start portmaster-core
|
||||||
|
|
||||||
CACHE --sharing shared "$GOCACHE"
|
CACHE --sharing shared "$GOCACHE"
|
||||||
CACHE --sharing shared "$GOMODCACHE"
|
CACHE --sharing shared "$GOMODCACHE"
|
||||||
|
@ -182,6 +248,7 @@ go-test:
|
||||||
|
|
||||||
go-test-all:
|
go-test-all:
|
||||||
FROM ${work_image}
|
FROM ${work_image}
|
||||||
|
ARG --required architectures
|
||||||
|
|
||||||
FOR arch IN ${architectures}
|
FOR arch IN ${architectures}
|
||||||
DO +RUST_TO_GO_ARCH_STRING --rustTarget="${arch}"
|
DO +RUST_TO_GO_ARCH_STRING --rustTarget="${arch}"
|
||||||
|
@ -197,6 +264,7 @@ go-lint:
|
||||||
# Builds portmaster-start, portmaster-core, hub and notifier for all supported platforms
|
# Builds portmaster-start, portmaster-core, hub and notifier for all supported platforms
|
||||||
go-release:
|
go-release:
|
||||||
FROM ${work_image}
|
FROM ${work_image}
|
||||||
|
ARG --required architectures
|
||||||
|
|
||||||
FOR arch IN ${architectures}
|
FOR arch IN ${architectures}
|
||||||
DO +RUST_TO_GO_ARCH_STRING --rustTarget="${arch}"
|
DO +RUST_TO_GO_ARCH_STRING --rustTarget="${arch}"
|
||||||
|
@ -218,11 +286,6 @@ go-build-utils:
|
||||||
BUILD +go-build --CMDS="" --GOARCH=amd64 --GOOS=linux
|
BUILD +go-build --CMDS="" --GOARCH=amd64 --GOOS=linux
|
||||||
BUILD +go-build --CMDS="" --GOARCH=amd64 --GOOS=windows
|
BUILD +go-build --CMDS="" --GOARCH=amd64 --GOOS=windows
|
||||||
|
|
||||||
# All targets that should run in CI for go.
|
|
||||||
go-ci:
|
|
||||||
BUILD +go-release
|
|
||||||
BUILD +go-test
|
|
||||||
|
|
||||||
# Prepares the angular project by installing dependencies
|
# Prepares the angular project by installing dependencies
|
||||||
angular-deps:
|
angular-deps:
|
||||||
FROM ${node_builder_image}
|
FROM ${node_builder_image}
|
||||||
|
@ -302,9 +365,6 @@ assets:
|
||||||
rust-base:
|
rust-base:
|
||||||
FROM ${rust_builder_image}
|
FROM ${rust_builder_image}
|
||||||
|
|
||||||
RUN dpkg --add-architecture armhf
|
|
||||||
RUN dpkg --add-architecture arm64
|
|
||||||
|
|
||||||
RUN apt-get update -qq
|
RUN apt-get update -qq
|
||||||
|
|
||||||
# Tools and libraries required for cross-compilation
|
# Tools and libraries required for cross-compilation
|
||||||
|
@ -318,74 +378,39 @@ rust-base:
|
||||||
gcc-multilib \
|
gcc-multilib \
|
||||||
linux-libc-dev \
|
linux-libc-dev \
|
||||||
linux-libc-dev-amd64-cross \
|
linux-libc-dev-amd64-cross \
|
||||||
linux-libc-dev-arm64-cross \
|
|
||||||
linux-libc-dev-armel-cross \
|
|
||||||
linux-libc-dev-armhf-cross \
|
|
||||||
build-essential \
|
build-essential \
|
||||||
curl \
|
curl \
|
||||||
wget \
|
wget \
|
||||||
file \
|
file \
|
||||||
libsoup-3.0-dev \
|
libsoup-3.0-dev \
|
||||||
libwebkit2gtk-4.1-dev
|
libwebkit2gtk-4.1-dev \
|
||||||
|
gcc-mingw-w64-x86-64 \
|
||||||
|
zip
|
||||||
|
|
||||||
# Install library dependencies for all supported architectures
|
# Install library dependencies for all supported architectures
|
||||||
# required for succesfully linking.
|
# required for succesfully linking.
|
||||||
FOR arch IN amd64 arm64 armhf
|
|
||||||
RUN apt-get install --no-install-recommends -qq \
|
RUN apt-get install --no-install-recommends -qq \
|
||||||
libsoup-3.0-0:${arch} \
|
libsoup-3.0-0 \
|
||||||
libwebkit2gtk-4.1-0:${arch} \
|
libwebkit2gtk-4.1-0 \
|
||||||
libssl3:${arch} \
|
libssl3 \
|
||||||
libayatana-appindicator3-1:${arch} \
|
libayatana-appindicator3-1 \
|
||||||
librsvg2-bin:${arch} \
|
librsvg2-bin \
|
||||||
libgtk-3-0:${arch} \
|
libgtk-3-0 \
|
||||||
libjavascriptcoregtk-4.1-0:${arch} \
|
libjavascriptcoregtk-4.1-0 \
|
||||||
libssl-dev:${arch} \
|
libssl-dev \
|
||||||
libayatana-appindicator3-dev:${arch} \
|
libayatana-appindicator3-dev \
|
||||||
librsvg2-dev:${arch} \
|
librsvg2-dev \
|
||||||
libgtk-3-dev:${arch} \
|
libgtk-3-dev \
|
||||||
libjavascriptcoregtk-4.1-dev:${arch}
|
libjavascriptcoregtk-4.1-dev
|
||||||
END
|
|
||||||
|
|
||||||
# Note(ppacher): I've no idea why we need to explicitly create those symlinks:
|
|
||||||
# Some how all the other libs work but libsoup and libwebkit2gtk do not create the link file
|
|
||||||
RUN cd /usr/lib/aarch64-linux-gnu && \
|
|
||||||
ln -s libwebkit2gtk-4.1.so.0 libwebkit2gtk-4.1.so && \
|
|
||||||
ln -s libsoup-3.0.so.0 libsoup-3.0.so
|
|
||||||
|
|
||||||
RUN cd /usr/lib/arm-linux-gnueabihf && \
|
|
||||||
ln -s libwebkit2gtk-4.1.so.0 libwebkit2gtk-4.1.so && \
|
|
||||||
ln -s libsoup-3.0.so.0 libsoup-3.0.so
|
|
||||||
|
|
||||||
# For what ever reason trying to install the gcc compilers together with the above
|
|
||||||
# command makes apt fail due to conflicts with gcc-multilib. Installing in a separate
|
|
||||||
# step seems to work ...
|
|
||||||
RUN apt-get install --no-install-recommends -qq \
|
|
||||||
g++-mingw-w64-x86-64 \
|
|
||||||
gcc-aarch64-linux-gnu \
|
|
||||||
gcc-arm-none-eabi \
|
|
||||||
gcc-arm-linux-gnueabi \
|
|
||||||
gcc-arm-linux-gnueabihf \
|
|
||||||
libc6-dev-arm64-cross \
|
|
||||||
libc6-dev-armel-cross \
|
|
||||||
libc6-dev-armhf-cross \
|
|
||||||
libc6-dev-amd64-cross
|
|
||||||
|
|
||||||
# Add some required rustup components
|
# Add some required rustup components
|
||||||
RUN rustup component add clippy
|
RUN rustup component add clippy
|
||||||
RUN rustup component add rustfmt
|
RUN rustup component add rustfmt
|
||||||
|
|
||||||
# Install architecture targets
|
|
||||||
FOR arch IN ${architectures}
|
|
||||||
RUN rustup target add ${arch}
|
|
||||||
END
|
|
||||||
|
|
||||||
DO rust+INIT --keep_fingerprints=true
|
DO rust+INIT --keep_fingerprints=true
|
||||||
|
|
||||||
# For now we need tauri-cli 1.5 for bulding
|
# For now we need tauri-cli 2.0.0 for bulding
|
||||||
DO rust+CARGO --args="install tauri-cli --version ^1.5.11"
|
DO rust+CARGO --args="install tauri-cli --version ^2.0.0-beta"
|
||||||
|
|
||||||
# Required for cross compilation to work.
|
|
||||||
ENV PKG_CONFIG_ALLOW_CROSS=1
|
|
||||||
|
|
||||||
# Explicitly cache here.
|
# Explicitly cache here.
|
||||||
SAVE IMAGE --cache-hint
|
SAVE IMAGE --cache-hint
|
||||||
|
@ -398,7 +423,7 @@ tauri-src:
|
||||||
# --keep-ts is necessary to ensure that the timestamps of the source files
|
# --keep-ts is necessary to ensure that the timestamps of the source files
|
||||||
# are preserved such that Rust's incremental compilation works correctly.
|
# are preserved such that Rust's incremental compilation works correctly.
|
||||||
COPY --keep-ts ./desktop/tauri/ .
|
COPY --keep-ts ./desktop/tauri/ .
|
||||||
COPY assets/data ./assets
|
COPY assets/data ./../../assets/data
|
||||||
COPY packaging/linux ./../../packaging/linux
|
COPY packaging/linux ./../../packaging/linux
|
||||||
COPY (+angular-project/output/tauri-builtin --project=tauri-builtin --dist=./dist/tauri-builtin --configuration=production --baseHref="/") ./../angular/dist/tauri-builtin
|
COPY (+angular-project/output/tauri-builtin --project=tauri-builtin --dist=./dist/tauri-builtin --configuration=production --baseHref="/") ./../angular/dist/tauri-builtin
|
||||||
|
|
||||||
|
@ -411,13 +436,151 @@ tauri-build:
|
||||||
FROM +tauri-src
|
FROM +tauri-src
|
||||||
|
|
||||||
ARG --required target
|
ARG --required target
|
||||||
ARG output=".*/release/(([^\./]+|([^\./]+\.(dll|exe)))|bundle/.*\.(deb|msi|AppImage))"
|
ARG output=".*/release/(([^\./]+|([^\./]+\.(dll|exe)))|bundle/(deb|rpm)/.*\.(deb|rpm))"
|
||||||
ARG bundle="none"
|
ARG bundle="none"
|
||||||
|
|
||||||
|
|
||||||
# if we want tauri to create the installer bundles we also need to provide all external binaries
|
# if we want tauri to create the installer bundles we also need to provide all external binaries
|
||||||
# we need to do some magic here because tauri expects the binaries to include the rust target tripple.
|
# we need to do some magic here because tauri expects the binaries to include the rust target tripple.
|
||||||
# We already knwo that triple because it's a required argument. From that triple, we use +RUST_TO_GO_ARCH_STRING
|
# We already know that triple because it's a required argument. From that triple, we use +RUST_TO_GO_ARCH_STRING
|
||||||
|
# function from below to parse the triple and guess wich GOOS and GOARCH we need.
|
||||||
|
RUN mkdir /tmp/gobuild
|
||||||
|
RUN mkdir ./binaries
|
||||||
|
|
||||||
|
DO +RUST_TO_GO_ARCH_STRING --rustTarget="${target}"
|
||||||
|
RUN echo "GOOS=${GOOS} GOARCH=${GOARCH} GOARM=${GOARM} GO_ARCH_STRING=${GO_ARCH_STRING}"
|
||||||
|
|
||||||
|
# Our tauri app has externalBins configured so tauri will try to embed them when it finished compiling
|
||||||
|
# the app. Make sure we copy portmaster-start and portmaster-core in all architectures supported.
|
||||||
|
# See documentation for externalBins for more information on how tauri searches for the binaries.
|
||||||
|
COPY (+go-build/output --CMDS="portmaster-start portmaster-core" --GOOS="${GOOS}" --GOARCH="${GOARCH}" --GOARM="${GOARM}") /tmp/gobuild
|
||||||
|
|
||||||
|
# Place them in the correct folder with the rust target tripple attached.
|
||||||
|
FOR bin IN $(ls /tmp/gobuild)
|
||||||
|
# ${bin$.*} does not work in SET commands unfortunately so we use a shell
|
||||||
|
# snippet here:
|
||||||
|
RUN set -e ; \
|
||||||
|
dest="./binaries/${bin}-${target}" ; \
|
||||||
|
if [ -z "${bin##*.exe}" ]; then \
|
||||||
|
dest="./binaries/${bin%.*}-${target}.exe" ; \
|
||||||
|
fi ; \
|
||||||
|
cp "/tmp/gobuild/${bin}" "${dest}" ;
|
||||||
|
END
|
||||||
|
|
||||||
|
# Just for debugging ...
|
||||||
|
# RUN ls -R ./binaries
|
||||||
|
|
||||||
|
# The following is exected to work but doesn't. for whatever reason cargo-sweep errors out on the windows-toolchain.
|
||||||
|
#
|
||||||
|
# DO rust+CARGO --args="tauri build --bundles none --ci --target=${target}" --output="release/[^/\.]+"
|
||||||
|
#
|
||||||
|
# For, now, we just directly mount the rust target cache and call cargo ourself.
|
||||||
|
|
||||||
|
DO rust+SET_CACHE_MOUNTS_ENV
|
||||||
|
RUN rustup target add "${target}"
|
||||||
|
RUN --mount=$EARTHLY_RUST_TARGET_CACHE cargo tauri build --ci --target="${target}"
|
||||||
|
DO rust+COPY_OUTPUT --output="${output}"
|
||||||
|
|
||||||
|
# BUG(cross-compilation):
|
||||||
|
#
|
||||||
|
# The above command seems to correctly compile for all architectures we want to support but fails during
|
||||||
|
# linking since the target libaries are not available for the requested platforms. Maybe we need to download
|
||||||
|
# the, manually ...
|
||||||
|
#
|
||||||
|
# The earthly rust lib also has support for using cross-rs for cross-compilation but that fails due to the
|
||||||
|
# fact that cross-rs base docker images used for building are heavily outdated (latest = ubunut:16.0, main = ubuntu:20.04)
|
||||||
|
# which does not ship recent enough glib versions (our glib dependency needs glib>2.70 but ubunut:20.04 only ships 2.64)
|
||||||
|
#
|
||||||
|
# The following would use the CROSS function from the earthly lib, this
|
||||||
|
# DO rust+CROSS --target="${target}"
|
||||||
|
|
||||||
|
RUN echo output: $(ls -R "target/${target}/release")
|
||||||
|
LET outbin="error"
|
||||||
|
FOR bin IN "portmaster Portmaster.exe WebView2Loader.dll"
|
||||||
|
# Modify output binary.
|
||||||
|
SET outbin="${bin}"
|
||||||
|
IF [ "${bin}" = "portmaster" ]
|
||||||
|
SET outbin="portmaster-app"
|
||||||
|
ELSE IF [ "${bin}" = "Portmaster.exe" ]
|
||||||
|
SET outbin="portmaster-app.exe"
|
||||||
|
END
|
||||||
|
# Save output binary as local artifact.
|
||||||
|
SAVE ARTIFACT --if-exists --keep-ts "target/${target}/release/${bin}" AS LOCAL "${outputDir}/${GO_ARCH_STRING}/${outbin}"
|
||||||
|
END
|
||||||
|
SAVE ARTIFACT --if-exists --keep-ts "target/${target}/release/bundle/deb/*.deb" AS LOCAL "${outputDir}/${GO_ARCH_STRING}/"
|
||||||
|
SAVE ARTIFACT --if-exists --keep-ts "target/${target}/release/bundle/rpm/*.rpm" AS LOCAL "${outputDir}/${GO_ARCH_STRING}/"
|
||||||
|
|
||||||
|
tauri-windows-release-bundle:
|
||||||
|
FROM +tauri-src
|
||||||
|
|
||||||
|
ARG target="x86_64-pc-windows-gnu"
|
||||||
|
ARG output=".*/release/(([^\./]+|([^\./]+\.(dll|exe))))"
|
||||||
|
ARG bundle="none"
|
||||||
|
|
||||||
|
ARG GOOS=windows
|
||||||
|
ARG GOARCH=amd64
|
||||||
|
ARG GOARM
|
||||||
|
|
||||||
|
# The binaries will not be used but we still need to create them. Tauri will check for them.
|
||||||
|
RUN mkdir /tmp/gobuild
|
||||||
|
RUN mkdir ./binaries
|
||||||
|
|
||||||
|
DO +RUST_TO_GO_ARCH_STRING --rustTarget="${target}"
|
||||||
|
RUN echo "GOOS=${GOOS} GOARCH=${GOARCH} GOARM=${GOARM} GO_ARCH_STRING=${GO_ARCH_STRING}"
|
||||||
|
|
||||||
|
# Our tauri app has externalBins configured so tauri will look for them when it finished compiling
|
||||||
|
# the app. Make sure we copy portmaster-start and portmaster-core in all architectures supported.
|
||||||
|
# See documentation for externalBins for more information on how tauri searches for the binaries.
|
||||||
|
COPY (+go-build/output --GOOS="${GOOS}" --CMDS="portmaster-start portmaster-core" --GOARCH="${GOARCH}" --GOARM="${GOARM}") /tmp/gobuild
|
||||||
|
|
||||||
|
# Place them in the correct folder with the rust target tripple attached.
|
||||||
|
FOR bin IN $(ls /tmp/gobuild)
|
||||||
|
# ${bin$.*} does not work in SET commands unfortunately so we use a shell
|
||||||
|
# snippet here:
|
||||||
|
RUN set -e ; \
|
||||||
|
dest="./binaries/${bin}-${target}" ; \
|
||||||
|
if [ -z "${bin##*.exe}" ]; then \
|
||||||
|
dest="./binaries/${bin%.*}-${target}.exe" ; \
|
||||||
|
fi ; \
|
||||||
|
cp "/tmp/gobuild/${bin}" "${dest}" ;
|
||||||
|
END
|
||||||
|
|
||||||
|
# Just for debugging ...
|
||||||
|
# RUN ls -R ./binaries
|
||||||
|
|
||||||
|
DO rust+SET_CACHE_MOUNTS_ENV
|
||||||
|
RUN rustup target add "${target}"
|
||||||
|
RUN --mount=$EARTHLY_RUST_TARGET_CACHE cargo tauri build --no-bundle --ci --target="${target}"
|
||||||
|
DO rust+COPY_OUTPUT --output="${output}"
|
||||||
|
|
||||||
|
# Get version from git.
|
||||||
|
COPY .git .
|
||||||
|
LET version = "$(git tag --points-at || true)"
|
||||||
|
IF [ -z "${version}" ]
|
||||||
|
LET dev_version = "$(git describe --tags --first-parent --abbrev=0 || true)"
|
||||||
|
IF [ -n "${dev_version}" ]
|
||||||
|
SET version = "${dev_version}"
|
||||||
|
END
|
||||||
|
END
|
||||||
|
IF [ -z "${version}" ]
|
||||||
|
SET version = "v0.0.0"
|
||||||
|
END
|
||||||
|
ENV VERSION="${version}"
|
||||||
|
RUN echo "Version: $VERSION"
|
||||||
|
ENV VERSION_SUFFIX="$(echo $VERSION | tr '.' '-')"
|
||||||
|
RUN echo "Version Suffix: $VERSION_SUFFIX"
|
||||||
|
|
||||||
|
RUN echo output: $(ls -R "target/${target}/release")
|
||||||
|
RUN mv "target/${target}/release/app.exe" "target/${target}/release/portmaster-app_${VERSION_SUFFIX}.exe"
|
||||||
|
RUN zip "target/${target}/release/portmaster-app_${VERSION_SUFFIX}.zip" "target/${target}/release/portmaster-app_${VERSION_SUFFIX}.exe" -j portmaster-app${VERSION_SUFFIX}.exe "target/${target}/release/WebView2Loader.dll" -j WebView2Loader.dll
|
||||||
|
SAVE ARTIFACT --if-exists "target/${target}/release/portmaster-app_${VERSION_SUFFIX}.zip" AS LOCAL "${outputDir}/${GO_ARCH_STRING}/"
|
||||||
|
|
||||||
|
tauri-prep-windows:
|
||||||
|
FROM +angular-base --configuration=production
|
||||||
|
ARG target="x86_64-pc-windows-msvc"
|
||||||
|
|
||||||
|
# if we want tauri to create the installer bundles we also need to provide all external binaries
|
||||||
|
# we need to do some magic here because tauri expects the binaries to include the rust target tripple.
|
||||||
|
# We already know that triple because it's a required argument. From that triple, we use +RUST_TO_GO_ARCH_STRING
|
||||||
# function from below to parse the triple and guess wich GOOS and GOARCH we need.
|
# function from below to parse the triple and guess wich GOOS and GOARCH we need.
|
||||||
RUN mkdir /tmp/gobuild
|
RUN mkdir /tmp/gobuild
|
||||||
RUN mkdir ./binaries
|
RUN mkdir ./binaries
|
||||||
|
@ -443,55 +606,29 @@ tauri-build:
|
||||||
cp "/tmp/gobuild/${bin}" "${dest}" ;
|
cp "/tmp/gobuild/${bin}" "${dest}" ;
|
||||||
END
|
END
|
||||||
|
|
||||||
|
# Copy source
|
||||||
|
COPY --keep-ts ./desktop/tauri/src-tauri src-tauri
|
||||||
|
COPY --keep-ts ./assets assets
|
||||||
|
|
||||||
|
# Build UI
|
||||||
|
ENV NODE_ENV="production"
|
||||||
|
RUN --no-cache ./node_modules/.bin/ng build --configuration production --base-href / "tauri-builtin"
|
||||||
|
|
||||||
# Just for debugging ...
|
# Just for debugging ...
|
||||||
RUN ls -R ./binaries
|
# RUN ls -R ./binaries
|
||||||
|
# RUN ls -R ./dist
|
||||||
|
|
||||||
# The following is exected to work but doesn't. for whatever reason cargo-sweep errors out on the windows-toolchain.
|
SAVE ARTIFACT "./dist/tauri-builtin" AS LOCAL "${outputDir}/${GO_ARCH_STRING}/desktop/angular/dist/"
|
||||||
#
|
SAVE ARTIFACT "./src-tauri" AS LOCAL "${outputDir}/${GO_ARCH_STRING}/desktop/tauri/src-tauri"
|
||||||
# DO rust+CARGO --args="tauri build --bundles none --ci --target=${target}" --output="release/[^/\.]+"
|
SAVE ARTIFACT "./binaries" AS LOCAL "${outputDir}/${GO_ARCH_STRING}/desktop/tauri/src-tauri/"
|
||||||
#
|
SAVE ARTIFACT "./assets" AS LOCAL "${outputDir}/${GO_ARCH_STRING}/assets"
|
||||||
# For, now, we just directly mount the rust target cache and call cargo ourself.
|
|
||||||
|
|
||||||
DO rust+SET_CACHE_MOUNTS_ENV
|
|
||||||
RUN --mount=$EARTHLY_RUST_TARGET_CACHE cargo tauri build --bundles "${bundle}" --ci --target="${target}"
|
|
||||||
DO rust+COPY_OUTPUT --output="${output}"
|
|
||||||
|
|
||||||
# BUG(cross-compilation):
|
|
||||||
#
|
|
||||||
# The above command seems to correctly compile for all architectures we want to support but fails during
|
|
||||||
# linking since the target libaries are not available for the requested platforms. Maybe we need to download
|
|
||||||
# the, manually ...
|
|
||||||
#
|
|
||||||
# The earthly rust lib also has support for using cross-rs for cross-compilation but that fails due to the
|
|
||||||
# fact that cross-rs base docker images used for building are heavily outdated (latest = ubunut:16.0, main = ubuntu:20.04)
|
|
||||||
# which does not ship recent enough glib versions (our glib dependency needs glib>2.70 but ubunut:20.04 only ships 2.64)
|
|
||||||
#
|
|
||||||
# The following would use the CROSS function from the earthly lib, this
|
|
||||||
# DO rust+CROSS --target="${target}"
|
|
||||||
|
|
||||||
# RUN echo output: $(ls "target/${target}/release")
|
|
||||||
LET outbin="error"
|
|
||||||
FOR bin IN "portmaster Portmaster.exe WebView2Loader.dll"
|
|
||||||
# Modify output binary.
|
|
||||||
SET outbin="${bin}"
|
|
||||||
IF [ "${bin}" = "portmaster" ]
|
|
||||||
SET outbin="portmaster-app"
|
|
||||||
ELSE IF [ "${bin}" = "Portmaster.exe" ]
|
|
||||||
SET outbin="portmaster-app.exe"
|
|
||||||
END
|
|
||||||
# Save output binary as local artifact.
|
|
||||||
IF [ -f "target/${target}/release/${bin}" ]
|
|
||||||
SAVE ARTIFACT --keep-ts "target/${target}/release/${bin}" AS LOCAL "${outputDir}/${GO_ARCH_STRING}/${outbin}"
|
|
||||||
END
|
|
||||||
END
|
|
||||||
|
|
||||||
tauri-release:
|
tauri-release:
|
||||||
FROM ${work_image}
|
FROM ${work_image}
|
||||||
|
ARG --required architectures
|
||||||
ARG bundle="none"
|
|
||||||
|
|
||||||
FOR arch IN ${architectures}
|
FOR arch IN ${architectures}
|
||||||
BUILD +tauri-build --target="${arch}" --bundle="${bundle}"
|
BUILD +tauri-build --target="${arch}"
|
||||||
END
|
END
|
||||||
|
|
||||||
kext-build:
|
kext-build:
|
||||||
|
@ -515,21 +652,6 @@ kext-build:
|
||||||
|
|
||||||
SAVE ARTIFACT --keep-ts "portmaster-kext-release-bundle.zip" AS LOCAL "${outputDir}/windows_amd64/portmaster-kext-release-bundle.zip"
|
SAVE ARTIFACT --keep-ts "portmaster-kext-release-bundle.zip" AS LOCAL "${outputDir}/windows_amd64/portmaster-kext-release-bundle.zip"
|
||||||
|
|
||||||
build:
|
|
||||||
BUILD +go-release
|
|
||||||
BUILD +angular-release
|
|
||||||
BUILD +tauri-release
|
|
||||||
BUILD +assets
|
|
||||||
|
|
||||||
release:
|
|
||||||
LOCALLY
|
|
||||||
|
|
||||||
IF ! git diff --quiet
|
|
||||||
RUN echo -e "\033[1;31m Refusing to release a dirty git repository. Please commit your local changes first! \033[0m" ; exit 1
|
|
||||||
END
|
|
||||||
|
|
||||||
BUILD +build
|
|
||||||
|
|
||||||
|
|
||||||
# Takes GOOS, GOARCH and optionally GOARM and creates a string representation for file-names.
|
# Takes GOOS, GOARCH and optionally GOARM and creates a string representation for file-names.
|
||||||
# in the form of ${GOOS}_{GOARCH} if GOARM is empty, otherwise ${GOOS}_${GOARCH}v${GOARM}.
|
# in the form of ${GOOS}_{GOARCH} if GOARM is empty, otherwise ${GOOS}_${GOARCH}v${GOARM}.
|
||||||
|
|
14
assets/data/icons/generate_ico.sh
Normal file
|
@ -0,0 +1,14 @@
|
||||||
|
#!/bin/sh
|
||||||
|
|
||||||
|
# Traymenu icons. Sometimes to wrong size is selected so remove 256 and 64.
|
||||||
|
convert pm_dark_green_512.png -colors 256 -define icon:auto-resize=48,32,16 pm_dark_green.ico
|
||||||
|
convert pm_dark_blue_512.png -colors 256 -define icon:auto-resize=48,32,16 pm_dark_blue.ico
|
||||||
|
convert pm_dark_red_512.png -colors 256 -define icon:auto-resize=48,32,16 pm_dark_red.ico
|
||||||
|
convert pm_dark_yellow_512.png -colors 256 -define icon:auto-resize=48,32,16 pm_dark_yellow.ico
|
||||||
|
convert pm_light_blue_512.png -colors 256 -define icon:auto-resize=48,32,16 pm_light_blue.ico
|
||||||
|
convert pm_light_green_512.png -colors 256 -define icon:auto-resize=48,32,16 pm_light_green.ico
|
||||||
|
convert pm_light_red_512.png -colors 256 -define icon:auto-resize=48,32,16 pm_light_red.ico
|
||||||
|
convert pm_light_yellow_512.png -colors 256 -define icon:auto-resize=48,32,16 pm_light_yellow.ico
|
||||||
|
|
||||||
|
convert pm_dark_512.png -colors 256 -define icon:auto-resize=64,48,32,16 pm_dark.ico
|
||||||
|
convert pm_light_512.png -colors 256 -define icon:auto-resize=64,48,32,16 pm_light.ico
|
BIN
assets/data/icons/pm_dark.ico
Normal file
After Width: | Height: | Size: 31 KiB |
Before Width: | Height: | Size: 108 KiB |
BIN
assets/data/icons/pm_dark_blue.ico
Normal file
After Width: | Height: | Size: 15 KiB |
Before Width: | Height: | Size: 97 KiB |
BIN
assets/data/icons/pm_dark_green.ico
Normal file
After Width: | Height: | Size: 15 KiB |
Before Width: | Height: | Size: 110 KiB |
BIN
assets/data/icons/pm_dark_red.ico
Normal file
After Width: | Height: | Size: 15 KiB |
Before Width: | Height: | Size: 110 KiB |
BIN
assets/data/icons/pm_dark_yellow.ico
Normal file
After Width: | Height: | Size: 15 KiB |
Before Width: | Height: | Size: 109 KiB |
BIN
assets/data/icons/pm_light.ico
Normal file
After Width: | Height: | Size: 31 KiB |
Before Width: | Height: | Size: 108 KiB |
BIN
assets/data/icons/pm_light_blue.ico
Normal file
After Width: | Height: | Size: 15 KiB |
Before Width: | Height: | Size: 110 KiB |
BIN
assets/data/icons/pm_light_green.ico
Normal file
After Width: | Height: | Size: 15 KiB |
Before Width: | Height: | Size: 110 KiB |
BIN
assets/data/icons/pm_light_red.ico
Normal file
After Width: | Height: | Size: 15 KiB |
Before Width: | Height: | Size: 110 KiB |
BIN
assets/data/icons/pm_light_yellow.ico
Normal file
After Width: | Height: | Size: 15 KiB |
Before Width: | Height: | Size: 110 KiB |
|
@ -14,16 +14,16 @@ const (
|
||||||
|
|
||||||
// Icons.
|
// Icons.
|
||||||
var (
|
var (
|
||||||
//go:embed data/icons/pm_light_green_512.ico
|
//go:embed data/icons/pm_light_green_256.png
|
||||||
GreenICO []byte
|
GreenICO []byte
|
||||||
|
|
||||||
//go:embed data/icons/pm_light_yellow_512.ico
|
//go:embed data/icons/pm_light_yellow_256.png
|
||||||
YellowICO []byte
|
YellowICO []byte
|
||||||
|
|
||||||
//go:embed data/icons/pm_light_red_512.ico
|
//go:embed data/icons/pm_light_red_256.png
|
||||||
RedICO []byte
|
RedICO []byte
|
||||||
|
|
||||||
//go:embed data/icons/pm_light_blue_512.ico
|
//go:embed data/icons/pm_light_blue_256.png
|
||||||
BlueICO []byte
|
BlueICO []byte
|
||||||
|
|
||||||
// ColoredIcons holds all the icons as .ICOs
|
// ColoredIcons holds all the icons as .ICOs
|
||||||
|
|
|
@ -28,6 +28,7 @@ var (
|
||||||
maxRetries int
|
maxRetries int
|
||||||
dataRoot *utils.DirStructure
|
dataRoot *utils.DirStructure
|
||||||
logsRoot *utils.DirStructure
|
logsRoot *utils.DirStructure
|
||||||
|
forceOldUI bool
|
||||||
|
|
||||||
updateURLFlag string
|
updateURLFlag string
|
||||||
userAgentFlag string
|
userAgentFlag string
|
||||||
|
@ -74,6 +75,7 @@ func init() {
|
||||||
flags.StringVar(&userAgentFlag, "update-agent", "", "Set an alternative user agent for requests to the update server")
|
flags.StringVar(&userAgentFlag, "update-agent", "", "Set an alternative user agent for requests to the update server")
|
||||||
flags.IntVar(&maxRetries, "max-retries", 5, "Maximum number of retries when starting a Portmaster component")
|
flags.IntVar(&maxRetries, "max-retries", 5, "Maximum number of retries when starting a Portmaster component")
|
||||||
flags.BoolVar(&stdinSignals, "input-signals", false, "Emulate signals using stdin.")
|
flags.BoolVar(&stdinSignals, "input-signals", false, "Emulate signals using stdin.")
|
||||||
|
flags.BoolVar(&forceOldUI, "old-ui", false, "Use the old ui. (Beta)")
|
||||||
_ = rootCmd.MarkPersistentFlagDirname("data")
|
_ = rootCmd.MarkPersistentFlagDirname("data")
|
||||||
_ = flags.MarkHidden("input-signals")
|
_ = flags.MarkHidden("input-signals")
|
||||||
}
|
}
|
||||||
|
|
|
@ -29,6 +29,10 @@ const (
|
||||||
// This disables retrying and exits with an error code.
|
// This disables retrying and exits with an error code.
|
||||||
ControlledFailureExitCode = 24
|
ControlledFailureExitCode = 24
|
||||||
|
|
||||||
|
// StartOldUIExitCode is an exit code that is returned by the UI when there. This is manfully triaged by the user, if the new UI does not work for them.
|
||||||
|
StartOldUIExitCode = 77
|
||||||
|
MissingDependencyExitCode = 0xc0000135 // Windows STATUS_DLL_NOT_FOUND
|
||||||
|
|
||||||
exeSuffix = ".exe"
|
exeSuffix = ".exe"
|
||||||
zipSuffix = ".zip"
|
zipSuffix = ".zip"
|
||||||
)
|
)
|
||||||
|
@ -38,6 +42,8 @@ var (
|
||||||
onWindows = runtime.GOOS == "windows"
|
onWindows = runtime.GOOS == "windows"
|
||||||
stdinSignals bool
|
stdinSignals bool
|
||||||
childIsRunning = abool.NewBool(false)
|
childIsRunning = abool.NewBool(false)
|
||||||
|
|
||||||
|
fallBackToOldUI bool = false
|
||||||
)
|
)
|
||||||
|
|
||||||
// Options for starting component.
|
// Options for starting component.
|
||||||
|
@ -55,7 +61,21 @@ type Options struct {
|
||||||
RestartOnFail bool // Try restarting automatically, if the started component fails.
|
RestartOnFail bool // Try restarting automatically, if the started component fails.
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// This is a temp value that will be used to test the new UI in beta.
|
||||||
|
var app2Options = Options{
|
||||||
|
Name: "Portmaster App2",
|
||||||
|
Identifier: "app2/portmaster-app",
|
||||||
|
AllowDownload: false,
|
||||||
|
AllowHidingWindow: false,
|
||||||
|
RestartOnFail: true,
|
||||||
|
}
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
|
// Make sure the new UI has a proper extension.
|
||||||
|
if onWindows {
|
||||||
|
app2Options.Identifier += ".zip"
|
||||||
|
}
|
||||||
|
|
||||||
registerComponent([]Options{
|
registerComponent([]Options{
|
||||||
{
|
{
|
||||||
Name: "Portmaster Core",
|
Name: "Portmaster Core",
|
||||||
|
@ -70,6 +90,7 @@ func init() {
|
||||||
Identifier: "app/portmaster-app.zip",
|
Identifier: "app/portmaster-app.zip",
|
||||||
AllowDownload: false,
|
AllowDownload: false,
|
||||||
AllowHidingWindow: false,
|
AllowHidingWindow: false,
|
||||||
|
RestartOnFail: true,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Name: "Portmaster Notifier",
|
Name: "Portmaster Notifier",
|
||||||
|
@ -89,6 +110,26 @@ func init() {
|
||||||
RestartOnFail: true,
|
RestartOnFail: true,
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
|
||||||
|
if onWindows {
|
||||||
|
registerComponent([]Options{
|
||||||
|
{
|
||||||
|
Name: "Portmaster App2",
|
||||||
|
Identifier: "app2/portmaster-app.zip",
|
||||||
|
AllowDownload: false,
|
||||||
|
AllowHidingWindow: false,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
registerComponent([]Options{
|
||||||
|
{
|
||||||
|
Name: "Portmaster App2",
|
||||||
|
Identifier: "app2/portmaster-app",
|
||||||
|
AllowDownload: false,
|
||||||
|
AllowHidingWindow: false,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func registerComponent(opts []Options) {
|
func registerComponent(opts []Options) {
|
||||||
|
@ -311,6 +352,14 @@ func persistOutputStreams(opts *Options, version string, cmd *exec.Cmd) (chan st
|
||||||
}
|
}
|
||||||
|
|
||||||
func execute(opts *Options, args []string) (cont bool, err error) {
|
func execute(opts *Options, args []string) (cont bool, err error) {
|
||||||
|
if !forceOldUI && registry.UsePreReleases && opts.ShortIdentifier == "app" {
|
||||||
|
// Check if new ui was already tried.
|
||||||
|
if !fallBackToOldUI {
|
||||||
|
opts = &app2Options
|
||||||
|
log.Println("Using new UI")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
file, err := registry.GetFile(
|
file, err := registry.GetFile(
|
||||||
helper.PlatformIdentifier(opts.Identifier),
|
helper.PlatformIdentifier(opts.Identifier),
|
||||||
)
|
)
|
||||||
|
@ -440,6 +489,12 @@ func parseExitError(err error) (restart bool, errWithCtx error) {
|
||||||
return true, nil
|
return true, nil
|
||||||
case ControlledFailureExitCode:
|
case ControlledFailureExitCode:
|
||||||
return false, errors.New("controlled failure, check logs")
|
return false, errors.New("controlled failure, check logs")
|
||||||
|
case StartOldUIExitCode:
|
||||||
|
fallBackToOldUI = true
|
||||||
|
return true, errors.New("user requested old UI")
|
||||||
|
case MissingDependencyExitCode:
|
||||||
|
fallBackToOldUI = true
|
||||||
|
return true, errors.New("new UI failed with missing dependency")
|
||||||
default:
|
default:
|
||||||
return true, fmt.Errorf("unknown exit code %w", exErr)
|
return true, fmt.Errorf("unknown exit code %w", exErr)
|
||||||
}
|
}
|
||||||
|
|
9035
desktop/angular/package-lock.json
generated
|
@ -37,13 +37,13 @@
|
||||||
"@fortawesome/free-brands-svg-icons": "^6.4.0",
|
"@fortawesome/free-brands-svg-icons": "^6.4.0",
|
||||||
"@fortawesome/free-regular-svg-icons": "^6.4.0",
|
"@fortawesome/free-regular-svg-icons": "^6.4.0",
|
||||||
"@fortawesome/free-solid-svg-icons": "^6.4.0",
|
"@fortawesome/free-solid-svg-icons": "^6.4.0",
|
||||||
"@tauri-apps/api": "^2.0.0-beta.3",
|
"@tauri-apps/api": ">=2.0.0-beta.0",
|
||||||
"@tauri-apps/plugin-cli": "^2.0.0-beta.1",
|
"@tauri-apps/plugin-cli": ">=2.0.0-beta.0",
|
||||||
"@tauri-apps/plugin-clipboard-manager": "^2.0.0-alpha.4",
|
"@tauri-apps/plugin-clipboard-manager": ">=2.0.0-beta.0",
|
||||||
"@tauri-apps/plugin-dialog": "^2.0.0-alpha.4",
|
"@tauri-apps/plugin-dialog": ">=2.0.0-beta.0",
|
||||||
"@tauri-apps/plugin-notification": "^2.0.0-alpha.4",
|
"@tauri-apps/plugin-notification": ">=2.0.0-beta.0",
|
||||||
"@tauri-apps/plugin-os": "^2.0.0-alpha.5",
|
"@tauri-apps/plugin-os": ">=2.0.0-beta.0",
|
||||||
"@tauri-apps/plugin-shell": "^2.0.0-alpha.4",
|
"@tauri-apps/plugin-shell": "^2.0.0-beta",
|
||||||
"autoprefixer": "^10.4.14",
|
"autoprefixer": "^10.4.14",
|
||||||
"d3": "^7.8.4",
|
"d3": "^7.8.4",
|
||||||
"data-urls": "^5.0.0",
|
"data-urls": "^5.0.0",
|
||||||
|
|
|
@ -28,7 +28,7 @@ function asyncInvoke<T>(method: string, args: object): Promise<T> {
|
||||||
if (typeof event.payload === 'object' && 'error' in event.payload) {
|
if (typeof event.payload === 'object' && 'error' in event.payload) {
|
||||||
reject(event.payload);
|
reject(event.payload);
|
||||||
return
|
return
|
||||||
};
|
}
|
||||||
|
|
||||||
resolve(event.payload);
|
resolve(event.payload);
|
||||||
})
|
})
|
||||||
|
@ -75,7 +75,7 @@ export class TauriIntegrationService implements IntegrationService {
|
||||||
}
|
}
|
||||||
|
|
||||||
getAppInfo(info: ProcessInfo): Promise<AppInfo> {
|
getAppInfo(info: ProcessInfo): Promise<AppInfo> {
|
||||||
return asyncInvoke("plugin:portmaster|get_app_info", {
|
return asyncInvoke("get_app_info", {
|
||||||
...info,
|
...info,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
@ -112,7 +112,7 @@ export class TauriIntegrationService implements IntegrationService {
|
||||||
|
|
||||||
async shouldShow(): Promise<boolean> {
|
async shouldShow(): Promise<boolean> {
|
||||||
try {
|
try {
|
||||||
const response = await invoke<string>("plugin:portmaster|should_show");
|
const response = await invoke<string>("should_show");
|
||||||
return response === "show";
|
return response === "show";
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.error(err);
|
console.error(err);
|
||||||
|
@ -122,7 +122,7 @@ export class TauriIntegrationService implements IntegrationService {
|
||||||
|
|
||||||
async shouldHandlePrompts(): Promise<boolean> {
|
async shouldHandlePrompts(): Promise<boolean> {
|
||||||
try {
|
try {
|
||||||
const response = await invoke<string>("plugin:portmaster|should_handle_prompts")
|
const response = await invoke<string>("should_handle_prompts")
|
||||||
return response === "true"
|
return response === "true"
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.error(err);
|
console.error(err);
|
||||||
|
@ -130,27 +130,27 @@ export class TauriIntegrationService implements IntegrationService {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
get_state(key: string): Promise<string> {
|
get_state(_: string): Promise<string> {
|
||||||
return invoke<string>("plugin:portmaster|get_state");
|
return invoke<string>("get_state");
|
||||||
}
|
}
|
||||||
|
|
||||||
set_state(key: string, value: string): Promise<void> {
|
set_state(key: string, value: string): Promise<void> {
|
||||||
return invoke<void>("plugin:portmaster|set_state", {
|
return invoke<void>("set_state", {
|
||||||
key,
|
key,
|
||||||
value
|
value
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
getServiceManagerStatus(): Promise<ServiceManagerStatus> {
|
getServiceManagerStatus(): Promise<ServiceManagerStatus> {
|
||||||
return asyncInvoke("plugin:portmaster|get_service_manager_status", {})
|
return asyncInvoke("get_service_manager_status", {})
|
||||||
}
|
}
|
||||||
|
|
||||||
startService(): Promise<any> {
|
startService(): Promise<any> {
|
||||||
return asyncInvoke("plugin:portmaster|start_service", {});
|
return asyncInvoke("start_service", {});
|
||||||
}
|
}
|
||||||
|
|
||||||
onExitRequest(cb: () => void): () => void {
|
onExitRequest(cb: () => void): () => void {
|
||||||
let unlisten: () => void = () => { };
|
let unlisten: () => void = () => undefined;
|
||||||
|
|
||||||
listen('exit-requested', () => {
|
listen('exit-requested', () => {
|
||||||
cb();
|
cb();
|
||||||
|
@ -189,7 +189,7 @@ export class TauriIntegrationService implements IntegrationService {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
let promptWindow = new Window("prompt", {
|
const promptWindow = new Window("prompt", {
|
||||||
alwaysOnTop: true,
|
alwaysOnTop: true,
|
||||||
decorations: false,
|
decorations: false,
|
||||||
minimizable: false,
|
minimizable: false,
|
||||||
|
|
|
@ -1 +0,0 @@
|
||||||
../../assets/data
|
|
4992
desktop/tauri/src-tauri/Cargo.lock
generated
|
@ -12,21 +12,21 @@ rust-version = "1.60"
|
||||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||||
|
|
||||||
[build-dependencies]
|
[build-dependencies]
|
||||||
tauri-build = { version = "2.0.0-alpha", features = [] }
|
tauri-build = { version = "2.0.0-beta.18", features = [] }
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
# Tauri
|
# Tauri
|
||||||
tauri = { version = "2.0.0-alpha", features = ["tray-icon", "icon-ico", "icon-png"] }
|
tauri = { version = "2.0.0-beta.23", features = ["tray-icon", "image-png", "config-json5"] }
|
||||||
tauri-plugin-shell = "2.0.0-alpha"
|
tauri-plugin-shell = "2.0.0-beta"
|
||||||
tauri-plugin-dialog = "2.0.0-alpha"
|
tauri-plugin-dialog = "2.0.0-beta"
|
||||||
tauri-plugin-clipboard-manager = "2.0.0-alpha"
|
tauri-plugin-clipboard-manager = "2.0.0-beta"
|
||||||
tauri-plugin-os = "2.0.0-alpha"
|
tauri-plugin-os = "2.0.0-beta"
|
||||||
tauri-plugin-single-instance = "2.0.0-alpha"
|
tauri-plugin-single-instance = "2.0.0-beta"
|
||||||
tauri-plugin-cli = "2.0.0-alpha"
|
tauri-plugin-cli = "2.0.0-beta"
|
||||||
tauri-plugin-notification = "2.0.0-alpha"
|
tauri-plugin-notification = "2.0.0-beta"
|
||||||
|
tauri-plugin-log = "2.0.0-beta"
|
||||||
|
|
||||||
# We still need the tauri-cli 1.5 for building
|
tauri-cli = "2.0.0-beta.21"
|
||||||
tauri-cli = "1.5.11"
|
|
||||||
|
|
||||||
# General
|
# General
|
||||||
serde_json = "1.0"
|
serde_json = "1.0"
|
||||||
|
@ -47,7 +47,10 @@ http = "1.0.0"
|
||||||
url = "2.5.0"
|
url = "2.5.0"
|
||||||
thiserror = "1.0"
|
thiserror = "1.0"
|
||||||
log = "0.4.21"
|
log = "0.4.21"
|
||||||
pretty_env_logger = "0.5.0"
|
reqwest = { version = "0.12" }
|
||||||
|
|
||||||
|
rfd = { version = "*", default-features = false, features = [ "tokio", "gtk3", "common-controls-v6" ] }
|
||||||
|
open = "5.1.3"
|
||||||
|
|
||||||
# Linux only
|
# Linux only
|
||||||
[target.'cfg(target_os = "linux")'.dependencies]
|
[target.'cfg(target_os = "linux")'.dependencies]
|
||||||
|
@ -62,6 +65,7 @@ gio-sys = "0.18.1"
|
||||||
[target.'cfg(target_os = "windows")'.dependencies]
|
[target.'cfg(target_os = "windows")'.dependencies]
|
||||||
windows-service = "0.6.0"
|
windows-service = "0.6.0"
|
||||||
windows = { version = "0.54.0", features = ["Win32_Foundation", "Win32_UI_Shell", "Win32_UI_WindowsAndMessaging"] }
|
windows = { version = "0.54.0", features = ["Win32_Foundation", "Win32_UI_Shell", "Win32_UI_WindowsAndMessaging"] }
|
||||||
|
tauri-winrt-notification = "0.3.0"
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
which = "6.0.0"
|
which = "6.0.0"
|
||||||
|
|
32
desktop/tauri/src-tauri/capabilities/default.json
Normal file
|
@ -0,0 +1,32 @@
|
||||||
|
{
|
||||||
|
"$schema": "../gen/schemas/desktop-schema.json",
|
||||||
|
"identifier": "default",
|
||||||
|
"description": "Capability for the main window",
|
||||||
|
"windows": [
|
||||||
|
"main",
|
||||||
|
"splash"
|
||||||
|
],
|
||||||
|
"remote": {
|
||||||
|
"urls": [
|
||||||
|
"http://localhost:817"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"permissions": [
|
||||||
|
"path:default",
|
||||||
|
"event:allow-listen",
|
||||||
|
"event:allow-unlisten",
|
||||||
|
"event:allow-emit",
|
||||||
|
"event:allow-emit-to",
|
||||||
|
"window:allow-hide",
|
||||||
|
"window:allow-show",
|
||||||
|
"window:allow-is-visible",
|
||||||
|
"window:allow-set-focus",
|
||||||
|
"app:default",
|
||||||
|
"image:default",
|
||||||
|
"resources:default",
|
||||||
|
"menu:default",
|
||||||
|
"tray:default",
|
||||||
|
"shell:allow-open",
|
||||||
|
"notification:default"
|
||||||
|
]
|
||||||
|
}
|
1
desktop/tauri/src-tauri/gen/schemas/acl-manifests.json
Normal file
1
desktop/tauri/src-tauri/gen/schemas/capabilities.json
Normal file
|
@ -0,0 +1 @@
|
||||||
|
{"default":{"identifier":"default","description":"Capability for the main window","remote":{"urls":["http://localhost:817"]},"local":true,"windows":["main","splash"],"permissions":["path:default","event:allow-listen","event:allow-unlisten","event:allow-emit","event:allow-emit-to","window:allow-hide","window:allow-show","window:allow-is-visible","window:allow-set-focus","app:default","image:default","resources:default","menu:default","tray:default","shell:allow-open","notification:default"]}}
|
2929
desktop/tauri/src-tauri/gen/schemas/desktop-schema.json
Normal file
2929
desktop/tauri/src-tauri/gen/schemas/linux-schema.json
Normal file
2929
desktop/tauri/src-tauri/gen/schemas/windows-schema.json
Normal file
|
@ -1,6 +1,8 @@
|
||||||
// Prevents additional console window on Windows in release, DO NOT REMOVE!!
|
// Prevents additional console window on Windows in release, DO NOT REMOVE!!
|
||||||
#![cfg_attr(not(debug_assertions), windows_subsystem = "windows")]
|
#![cfg_attr(not(debug_assertions), windows_subsystem = "windows")]
|
||||||
|
|
||||||
|
use std::time::Duration;
|
||||||
|
|
||||||
use tauri::{AppHandle, Manager, RunEvent, WindowEvent};
|
use tauri::{AppHandle, Manager, RunEvent, WindowEvent};
|
||||||
use tauri_plugin_cli::CliExt;
|
use tauri_plugin_cli::CliExt;
|
||||||
|
|
||||||
|
@ -16,7 +18,7 @@ mod portmaster;
|
||||||
mod traymenu;
|
mod traymenu;
|
||||||
mod window;
|
mod window;
|
||||||
|
|
||||||
use log::{debug, error, info, trace, warn};
|
use log::{debug, error, info};
|
||||||
use portmaster::PortmasterExt;
|
use portmaster::PortmasterExt;
|
||||||
use traymenu::setup_tray_menu;
|
use traymenu::setup_tray_menu;
|
||||||
use window::{close_splash_window, create_main_window};
|
use window::{close_splash_window, create_main_window};
|
||||||
|
@ -24,6 +26,8 @@ use window::{close_splash_window, create_main_window};
|
||||||
#[macro_use]
|
#[macro_use]
|
||||||
extern crate lazy_static;
|
extern crate lazy_static;
|
||||||
|
|
||||||
|
const FALLBACK_TO_OLD_UI_EXIT_CODE: i32 = 77;
|
||||||
|
|
||||||
#[derive(Clone, serde::Serialize)]
|
#[derive(Clone, serde::Serialize)]
|
||||||
struct Payload {
|
struct Payload {
|
||||||
args: Vec<String>,
|
args: Vec<String>,
|
||||||
|
@ -82,12 +86,38 @@ impl portmaster::Handler for WsHandler {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn show_webview_not_installed_dialog() -> i32 {
|
||||||
|
use rfd::MessageDialog;
|
||||||
|
|
||||||
|
let result = MessageDialog::new()
|
||||||
|
.set_title("Portmaster")
|
||||||
|
.set_description("Webkit is not installed. Please install it and run portmaster again")
|
||||||
|
.set_buttons(rfd::MessageButtons::OkCancelCustom(
|
||||||
|
"Go to install page".to_owned(),
|
||||||
|
"Use old UI".to_owned(),
|
||||||
|
))
|
||||||
|
.show();
|
||||||
|
println!("{:?}", result);
|
||||||
|
if let rfd::MessageDialogResult::Custom(result) = result {
|
||||||
|
if result.eq("Go to install page") {
|
||||||
|
_ = open::that("https://wiki.safing.io/en/Portmaster/Install/Webview");
|
||||||
|
std::thread::sleep(Duration::from_secs(2));
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return FALLBACK_TO_OLD_UI_EXIT_CODE;
|
||||||
|
}
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
pretty_env_logger::init();
|
if let Err(_) = tauri::webview_version() {
|
||||||
|
std::process::exit(show_webview_not_installed_dialog());
|
||||||
|
}
|
||||||
|
|
||||||
let app = tauri::Builder::default()
|
let app = tauri::Builder::default()
|
||||||
// Shell plugin for open_external support
|
// Shell plugin for open_external support
|
||||||
.plugin(tauri_plugin_shell::init())
|
.plugin(tauri_plugin_shell::init())
|
||||||
|
.plugin(tauri_plugin_log::Builder::default().build())
|
||||||
// Clipboard support
|
// Clipboard support
|
||||||
.plugin(tauri_plugin_clipboard_manager::init())
|
.plugin(tauri_plugin_clipboard_manager::init())
|
||||||
// Dialog (Save/Open) support
|
// Dialog (Save/Open) support
|
||||||
|
@ -102,16 +132,24 @@ fn main() {
|
||||||
.plugin(tauri_plugin_cli::init())
|
.plugin(tauri_plugin_cli::init())
|
||||||
// Notification support
|
// Notification support
|
||||||
.plugin(tauri_plugin_notification::init())
|
.plugin(tauri_plugin_notification::init())
|
||||||
// Our Portmaster Plugin that handles communication between tauri and our angular app.
|
.invoke_handler(tauri::generate_handler![
|
||||||
.plugin(portmaster::init())
|
portmaster::commands::get_app_info,
|
||||||
|
portmaster::commands::get_service_manager_status,
|
||||||
|
portmaster::commands::start_service,
|
||||||
|
portmaster::commands::get_state,
|
||||||
|
portmaster::commands::set_state,
|
||||||
|
portmaster::commands::should_show,
|
||||||
|
portmaster::commands::should_handle_prompts
|
||||||
|
])
|
||||||
// Setup the app an any listeners
|
// Setup the app an any listeners
|
||||||
.setup(|app| {
|
.setup(|app| {
|
||||||
setup_tray_menu(app)?;
|
setup_tray_menu(app)?;
|
||||||
|
portmaster::setup(app.handle().clone());
|
||||||
|
|
||||||
// Setup the single-instance event listener that will create/focus the main window
|
// Setup the single-instance event listener that will create/focus the main window
|
||||||
// or the splash-screen.
|
// or the splash-screen.
|
||||||
let handle = app.handle().clone();
|
let handle = app.handle().clone();
|
||||||
app.listen_global("single-instance", move |_event| {
|
app.listen_any("single-instance", move |_event| {
|
||||||
let _ = window::open_window(&handle);
|
let _ = window::open_window(&handle);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -196,17 +234,23 @@ fn main() {
|
||||||
);
|
);
|
||||||
|
|
||||||
api.prevent_close();
|
api.prevent_close();
|
||||||
if let Some(window) = handle.get_window(label.as_str()) {
|
if let Some(window) = handle.get_webview_window(label.as_str()) {
|
||||||
let _ = window.emit("exit-requested", "");
|
let result = window.emit("exit-requested", "");
|
||||||
|
if let Err(err) = result {
|
||||||
|
error!("failed to emit event: {}", err.to_string());
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
error!("window was None");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
_ => {}
|
_ => {}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
RunEvent::ExitRequested { api, .. } => {
|
// TODO(vladimir): why was this needed?
|
||||||
api.prevent_exit();
|
// RunEvent::ExitRequested { api, .. } => {
|
||||||
}
|
// api.prevent_exit();
|
||||||
|
// }
|
||||||
_ => {}
|
_ => {}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
use super::PortmasterPlugin;
|
use super::PortmasterInterface;
|
||||||
use crate::service::get_service_manager;
|
use crate::service::get_service_manager;
|
||||||
use crate::service::ServiceManager;
|
use crate::service::ServiceManager;
|
||||||
use log::debug;
|
use log::debug;
|
||||||
|
@ -15,7 +15,7 @@ pub struct Error {
|
||||||
#[tauri::command]
|
#[tauri::command]
|
||||||
pub fn should_show<R: Runtime>(
|
pub fn should_show<R: Runtime>(
|
||||||
_window: Window<R>,
|
_window: Window<R>,
|
||||||
portmaster: State<'_, PortmasterPlugin<R>>,
|
portmaster: State<'_, PortmasterInterface<R>>,
|
||||||
) -> Result {
|
) -> Result {
|
||||||
if portmaster.get_show_after_bootstrap() {
|
if portmaster.get_show_after_bootstrap() {
|
||||||
debug!("[tauri:rpc:should_show] application should show after bootstrap");
|
debug!("[tauri:rpc:should_show] application should show after bootstrap");
|
||||||
|
@ -31,7 +31,7 @@ pub fn should_show<R: Runtime>(
|
||||||
#[tauri::command]
|
#[tauri::command]
|
||||||
pub fn should_handle_prompts<R: Runtime>(
|
pub fn should_handle_prompts<R: Runtime>(
|
||||||
_window: Window<R>,
|
_window: Window<R>,
|
||||||
portmaster: State<'_, PortmasterPlugin<R>>,
|
portmaster: State<'_, PortmasterInterface<R>>,
|
||||||
) -> Result {
|
) -> Result {
|
||||||
if portmaster.handle_prompts.load(Ordering::Relaxed) {
|
if portmaster.handle_prompts.load(Ordering::Relaxed) {
|
||||||
Ok("true".to_string())
|
Ok("true".to_string())
|
||||||
|
@ -43,7 +43,7 @@ pub fn should_handle_prompts<R: Runtime>(
|
||||||
#[tauri::command]
|
#[tauri::command]
|
||||||
pub fn get_state<R: Runtime>(
|
pub fn get_state<R: Runtime>(
|
||||||
_window: Window<R>,
|
_window: Window<R>,
|
||||||
portmaster: State<'_, PortmasterPlugin<R>>,
|
portmaster: State<'_, PortmasterInterface<R>>,
|
||||||
key: String,
|
key: String,
|
||||||
) -> Result {
|
) -> Result {
|
||||||
let value = portmaster.get_state(key);
|
let value = portmaster.get_state(key);
|
||||||
|
@ -58,7 +58,7 @@ pub fn get_state<R: Runtime>(
|
||||||
#[tauri::command]
|
#[tauri::command]
|
||||||
pub fn set_state<R: Runtime>(
|
pub fn set_state<R: Runtime>(
|
||||||
_window: Window<R>,
|
_window: Window<R>,
|
||||||
portmaster: State<'_, PortmasterPlugin<R>>,
|
portmaster: State<'_, PortmasterInterface<R>>,
|
||||||
key: String,
|
key: String,
|
||||||
value: String,
|
value: String,
|
||||||
) -> Result {
|
) -> Result {
|
||||||
|
|
|
@ -13,7 +13,7 @@
|
||||||
/// in the crate root.
|
/// in the crate root.
|
||||||
// The commands module contains tauri commands that are available to Javascript
|
// The commands module contains tauri commands that are available to Javascript
|
||||||
// using the invoke() and our custom invokeAsync() command.
|
// using the invoke() and our custom invokeAsync() command.
|
||||||
mod commands;
|
pub mod commands;
|
||||||
|
|
||||||
// The websocket module spawns an async function on tauri's runtime that manages
|
// The websocket module spawns an async function on tauri's runtime that manages
|
||||||
// a persistent connection to the Portmaster websocket API and updates the tauri Portmaster
|
// a persistent connection to the Portmaster websocket API and updates the tauri Portmaster
|
||||||
|
@ -34,10 +34,9 @@ use std::{
|
||||||
use log::{debug, error};
|
use log::{debug, error};
|
||||||
use serde;
|
use serde;
|
||||||
use std::sync::Mutex;
|
use std::sync::Mutex;
|
||||||
use tauri::{
|
use tauri::{AppHandle, Manager, Runtime};
|
||||||
plugin::{Builder, TauriPlugin},
|
|
||||||
AppHandle, Manager, Runtime,
|
const PORTMASTER_BASE_URL: &'static str = "http://127.0.0.1:817/api/v1/";
|
||||||
};
|
|
||||||
|
|
||||||
pub trait Handler {
|
pub trait Handler {
|
||||||
fn on_connect(&mut self, cli: PortAPI) -> ();
|
fn on_connect(&mut self, cli: PortAPI) -> ();
|
||||||
|
@ -45,7 +44,7 @@ pub trait Handler {
|
||||||
fn name(&self) -> String;
|
fn name(&self) -> String;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct PortmasterPlugin<R: Runtime> {
|
pub struct PortmasterInterface<R: Runtime> {
|
||||||
#[allow(dead_code)]
|
#[allow(dead_code)]
|
||||||
app: AppHandle<R>,
|
app: AppHandle<R>,
|
||||||
|
|
||||||
|
@ -76,7 +75,7 @@ pub struct PortmasterPlugin<R: Runtime> {
|
||||||
should_show_after_bootstrap: AtomicBool,
|
should_show_after_bootstrap: AtomicBool,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<R: Runtime> PortmasterPlugin<R> {
|
impl<R: Runtime> PortmasterInterface<R> {
|
||||||
/// Returns a state stored in the portmaster plugin.
|
/// Returns a state stored in the portmaster plugin.
|
||||||
pub fn get_state(&self, key: String) -> Option<String> {
|
pub fn get_state(&self, key: String) -> Option<String> {
|
||||||
let map = self.state.lock();
|
let map = self.state.lock();
|
||||||
|
@ -124,7 +123,6 @@ impl<R: Runtime> PortmasterPlugin<R> {
|
||||||
}
|
}
|
||||||
|
|
||||||
handlers.push(Box::new(handler));
|
handlers.push(Box::new(handler));
|
||||||
|
|
||||||
debug!("number of registered handlers: {}", handlers.len());
|
debug!("number of registered handlers: {}", handlers.len());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -174,7 +172,7 @@ impl<R: Runtime> PortmasterPlugin<R> {
|
||||||
self.should_show_after_bootstrap.load(Ordering::Relaxed)
|
self.should_show_after_bootstrap.load(Ordering::Relaxed)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Tells the angular applicatoin to show the window by emitting an event.
|
/// Tells the angular application to show the window by emitting an event.
|
||||||
/// It calls set_show_after_bootstrap(true) automatically so the application
|
/// It calls set_show_after_bootstrap(true) automatically so the application
|
||||||
/// also shows after bootstrapping.
|
/// also shows after bootstrapping.
|
||||||
pub fn show_window(&self) {
|
pub fn show_window(&self) {
|
||||||
|
@ -184,8 +182,9 @@ impl<R: Runtime> PortmasterPlugin<R> {
|
||||||
// misses the event below because it's still bootstrapping.
|
// misses the event below because it's still bootstrapping.
|
||||||
self.set_show_after_bootstrap(true);
|
self.set_show_after_bootstrap(true);
|
||||||
|
|
||||||
// ignore the error here, there's nothing we could do about it anyways.
|
if let Err(err) = self.app.emit("portmaster:show", "") {
|
||||||
let _ = self.app.emit("portmaster:show", "");
|
error!("failed to emit show event: {}", err.to_string());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Enables or disables the SPN.
|
/// Enables or disables the SPN.
|
||||||
|
@ -206,12 +205,30 @@ impl<R: Runtime> PortmasterPlugin<R> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Send Shutdown request to portmaster
|
||||||
|
pub fn trigger_shutdown(&self) {
|
||||||
|
tauri::async_runtime::spawn(async move {
|
||||||
|
let client = reqwest::Client::new();
|
||||||
|
match client
|
||||||
|
.post(format!("{}core/shutdown", PORTMASTER_BASE_URL))
|
||||||
|
.send()
|
||||||
|
.await
|
||||||
|
{
|
||||||
|
Ok(v) => {
|
||||||
|
debug!("shutdown request sent {:?}", v);
|
||||||
|
}
|
||||||
|
Err(err) => {
|
||||||
|
error!("failed to send shutdown request {}", err);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
//// Internal functions
|
//// Internal functions
|
||||||
fn start_notification_handler(&self) {
|
fn start_notification_handler(&self) {
|
||||||
if let Some(api) = self.get_api() {
|
if let Some(api) = self.get_api() {
|
||||||
let cli = api.clone();
|
|
||||||
tauri::async_runtime::spawn(async move {
|
tauri::async_runtime::spawn(async move {
|
||||||
notifications::notification_handler(cli).await;
|
notifications::notification_handler(api).await;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -223,9 +240,10 @@ impl<R: Runtime> PortmasterPlugin<R> {
|
||||||
self.is_reachable.store(true, Ordering::Relaxed);
|
self.is_reachable.store(true, Ordering::Relaxed);
|
||||||
|
|
||||||
// store the new api client.
|
// store the new api client.
|
||||||
|
{
|
||||||
let mut guard = self.api.lock().unwrap();
|
let mut guard = self.api.lock().unwrap();
|
||||||
*guard = Some(api.clone());
|
*guard = Some(api.clone());
|
||||||
drop(guard);
|
}
|
||||||
|
|
||||||
// fire-off the notification handler.
|
// fire-off the notification handler.
|
||||||
if self.handle_notifications.load(Ordering::Relaxed) {
|
if self.handle_notifications.load(Ordering::Relaxed) {
|
||||||
|
@ -249,9 +267,10 @@ impl<R: Runtime> PortmasterPlugin<R> {
|
||||||
self.is_reachable.store(false, Ordering::Relaxed);
|
self.is_reachable.store(false, Ordering::Relaxed);
|
||||||
|
|
||||||
// clear the current api client reference.
|
// clear the current api client reference.
|
||||||
|
{
|
||||||
let mut guard = self.api.lock().unwrap();
|
let mut guard = self.api.lock().unwrap();
|
||||||
*guard = None;
|
*guard = None;
|
||||||
drop(guard);
|
}
|
||||||
|
|
||||||
if let Ok(mut handlers) = self.handlers.lock() {
|
if let Ok(mut handlers) = self.handlers.lock() {
|
||||||
for handler in handlers.iter_mut() {
|
for handler in handlers.iter_mut() {
|
||||||
|
@ -262,31 +281,20 @@ impl<R: Runtime> PortmasterPlugin<R> {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub trait PortmasterExt<R: Runtime> {
|
pub trait PortmasterExt<R: Runtime> {
|
||||||
fn portmaster(&self) -> &PortmasterPlugin<R>;
|
fn portmaster(&self) -> &PortmasterInterface<R>;
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(serde::Serialize, serde::Deserialize, Debug)]
|
#[derive(serde::Serialize, serde::Deserialize, Debug)]
|
||||||
pub struct Config {}
|
pub struct Config {}
|
||||||
|
|
||||||
impl<R: Runtime, T: Manager<R>> PortmasterExt<R> for T {
|
impl<R: Runtime, T: Manager<R>> PortmasterExt<R> for T {
|
||||||
fn portmaster(&self) -> &PortmasterPlugin<R> {
|
fn portmaster(&self) -> &PortmasterInterface<R> {
|
||||||
self.state::<PortmasterPlugin<R>>().inner()
|
self.state::<PortmasterInterface<R>>().inner()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn init<R: Runtime>() -> TauriPlugin<R, Option<Config>> {
|
pub fn setup(app: AppHandle) {
|
||||||
Builder::<R, Option<Config>>::new("portmaster")
|
let interface = PortmasterInterface {
|
||||||
.invoke_handler(tauri::generate_handler![
|
|
||||||
commands::get_app_info,
|
|
||||||
commands::get_service_manager_status,
|
|
||||||
commands::start_service,
|
|
||||||
commands::get_state,
|
|
||||||
commands::set_state,
|
|
||||||
commands::should_show,
|
|
||||||
commands::should_handle_prompts
|
|
||||||
])
|
|
||||||
.setup(|app, _api| {
|
|
||||||
let plugin = PortmasterPlugin {
|
|
||||||
app: app.clone(),
|
app: app.clone(),
|
||||||
state: Mutex::new(HashMap::new()),
|
state: Mutex::new(HashMap::new()),
|
||||||
is_reachable: AtomicBool::new(false),
|
is_reachable: AtomicBool::new(false),
|
||||||
|
@ -297,12 +305,8 @@ pub fn init<R: Runtime>() -> TauriPlugin<R, Option<Config>> {
|
||||||
should_show_after_bootstrap: AtomicBool::new(true),
|
should_show_after_bootstrap: AtomicBool::new(true),
|
||||||
};
|
};
|
||||||
|
|
||||||
app.manage(plugin);
|
app.manage(interface);
|
||||||
|
|
||||||
// fire of the websocket handler
|
// fire of the websocket handler
|
||||||
websocket::start_websocket_thread(app.clone());
|
websocket::start_websocket_thread(app.clone());
|
||||||
|
|
||||||
Ok(())
|
|
||||||
})
|
|
||||||
.build()
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,9 +3,7 @@ use crate::portapi::message::*;
|
||||||
use crate::portapi::models::notification::*;
|
use crate::portapi::models::notification::*;
|
||||||
use crate::portapi::types::*;
|
use crate::portapi::types::*;
|
||||||
use log::error;
|
use log::error;
|
||||||
use notify_rust;
|
|
||||||
use serde_json::json;
|
use serde_json::json;
|
||||||
#[allow(unused_imports)]
|
|
||||||
use tauri::async_runtime;
|
use tauri::async_runtime;
|
||||||
|
|
||||||
pub async fn notification_handler(cli: PortAPI) {
|
pub async fn notification_handler(cli: PortAPI) {
|
||||||
|
@ -34,10 +32,24 @@ pub async fn notification_handler(cli: PortAPI) {
|
||||||
if n.selected_action_id != "" {
|
if n.selected_action_id != "" {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
show_notification(&cli, key, n).await;
|
||||||
|
}
|
||||||
|
Err(err) => match err {
|
||||||
|
ParseError::JSON(err) => {
|
||||||
|
error!("failed to parse notification: {}", err);
|
||||||
|
}
|
||||||
|
_ => {
|
||||||
|
error!("unknown error when parsing notifications payload");
|
||||||
|
}
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// TODO(ppacher): keep a reference of open notifications and close them
|
#[cfg(target_os = "linux")]
|
||||||
// if the user reacted inside the UI:
|
pub async fn show_notification(cli: &PortAPI, key: String, n: Notification) {
|
||||||
|
|
||||||
let mut notif = notify_rust::Notification::new();
|
let mut notif = notify_rust::Notification::new();
|
||||||
notif.body(&n.message);
|
notif.body(&n.message);
|
||||||
notif.timeout(notify_rust::Timeout::Never); // TODO(ppacher): use n.expires to calculate the timeout.
|
notif.timeout(notify_rust::Timeout::Never); // TODO(ppacher): use n.expires to calculate the timeout.
|
||||||
|
@ -48,11 +60,12 @@ pub async fn notification_handler(cli: PortAPI) {
|
||||||
notif.action(&action.id, &action.text);
|
notif.action(&action.id, &action.text);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(target_os = "linux")]
|
|
||||||
{
|
{
|
||||||
let cli_clone = cli.clone();
|
let cli_clone = cli.clone();
|
||||||
async_runtime::spawn(async move {
|
async_runtime::spawn(async move {
|
||||||
let res = notif.show();
|
let res = notif.show();
|
||||||
|
// TODO(ppacher): keep a reference of open notifications and close them
|
||||||
|
// if the user reacted inside the UI:
|
||||||
match res {
|
match res {
|
||||||
Ok(handle) => {
|
Ok(handle) => {
|
||||||
handle.wait_for_action(|action| {
|
handle.wait_for_action(|action| {
|
||||||
|
@ -87,17 +100,46 @@ pub async fn notification_handler(cli: PortAPI) {
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Err(err) => match err {
|
|
||||||
ParseError::JSON(err) => {
|
#[cfg(target_os = "windows")]
|
||||||
error!("failed to parse notification: {}", err);
|
pub async fn show_notification(cli: &PortAPI, key: String, n: Notification) {
|
||||||
}
|
use tauri_winrt_notification::{Duration, Sound, Toast};
|
||||||
_ => {
|
|
||||||
error!("unknown error when parsing notifications payload");
|
let mut toast = Toast::new("io.safing.portmaster")
|
||||||
}
|
.title(&n.title)
|
||||||
},
|
.text1(&n.message)
|
||||||
}
|
.sound(Some(Sound::Default))
|
||||||
}
|
.duration(Duration::Long);
|
||||||
}
|
|
||||||
}
|
for action in n.actions {
|
||||||
|
toast = toast.add_button(&action.text, &action.id);
|
||||||
|
}
|
||||||
|
{
|
||||||
|
let cli = cli.clone();
|
||||||
|
toast = toast.on_activated(move |action| -> windows::core::Result<()> {
|
||||||
|
if let Some(value) = action {
|
||||||
|
let cli = cli.clone();
|
||||||
|
let key = key.clone();
|
||||||
|
async_runtime::spawn(async move {
|
||||||
|
let _ = cli
|
||||||
|
.request(Request::Update(
|
||||||
|
key,
|
||||||
|
Payload::JSON(
|
||||||
|
json!({
|
||||||
|
"SelectedActionID": value
|
||||||
|
})
|
||||||
|
.to_string(),
|
||||||
|
),
|
||||||
|
))
|
||||||
|
.await;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
// TODO(vladimir): If Action is None, the user clicked on the notification. Focus on the UI.
|
||||||
|
Ok(())
|
||||||
|
});
|
||||||
|
}
|
||||||
|
toast.show().expect("unable to send notification");
|
||||||
|
// TODO(vladimir): keep a reference of open notifications and close them
|
||||||
|
// if the user reacted inside the UI:
|
||||||
}
|
}
|
||||||
|
|
|
@ -12,7 +12,6 @@ use std::process::ExitStatus;
|
||||||
#[cfg(target_os = "linux")]
|
#[cfg(target_os = "linux")]
|
||||||
use crate::service::systemd::SystemdServiceManager;
|
use crate::service::systemd::SystemdServiceManager;
|
||||||
|
|
||||||
use log::info;
|
|
||||||
use thiserror::Error;
|
use thiserror::Error;
|
||||||
|
|
||||||
use self::status::StatusResult;
|
use self::status::StatusResult;
|
||||||
|
@ -63,7 +62,7 @@ pub fn get_service_manager() -> Result<impl ServiceManager> {
|
||||||
#[cfg(target_os = "linux")]
|
#[cfg(target_os = "linux")]
|
||||||
{
|
{
|
||||||
if SystemdServiceManager::is_installed() {
|
if SystemdServiceManager::is_installed() {
|
||||||
info!("system service manager: systemd");
|
log::info!("system service manager: systemd");
|
||||||
|
|
||||||
Ok(SystemdServiceManager {})
|
Ok(SystemdServiceManager {})
|
||||||
} else {
|
} else {
|
||||||
|
|
|
@ -1,16 +1,15 @@
|
||||||
use std::collections::HashMap;
|
use std::sync::atomic::AtomicBool;
|
||||||
use std::sync::Mutex;
|
use std::sync::Mutex;
|
||||||
|
use std::{collections::HashMap, sync::atomic::Ordering};
|
||||||
|
|
||||||
use log::{debug, error};
|
use log::{debug, error};
|
||||||
|
use tauri::tray::{MouseButton, MouseButtonState};
|
||||||
use tauri::{
|
use tauri::{
|
||||||
menu::{
|
image::Image,
|
||||||
CheckMenuItem, CheckMenuItemBuilder, MenuBuilder, MenuItemBuilder, PredefinedMenuItem,
|
menu::{MenuBuilder, MenuItem, MenuItemBuilder, PredefinedMenuItem, SubmenuBuilder},
|
||||||
SubmenuBuilder,
|
tray::{TrayIcon, TrayIconBuilder},
|
||||||
},
|
Wry,
|
||||||
tray::{ClickType, TrayIcon, TrayIconBuilder},
|
|
||||||
Icon, Manager, Wry,
|
|
||||||
};
|
};
|
||||||
use tauri_plugin_dialog::DialogExt;
|
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
portapi::{
|
portapi::{
|
||||||
|
@ -26,62 +25,90 @@ use crate::{
|
||||||
portmaster::PortmasterExt,
|
portmaster::PortmasterExt,
|
||||||
window::{create_main_window, may_navigate_to_ui, open_window},
|
window::{create_main_window, may_navigate_to_ui, open_window},
|
||||||
};
|
};
|
||||||
|
use tauri_plugin_dialog::DialogExt;
|
||||||
|
|
||||||
pub type AppIcon = TrayIcon<Wry>;
|
pub type AppIcon = TrayIcon<Wry>;
|
||||||
|
|
||||||
|
static SPN_STATE: AtomicBool = AtomicBool::new(false);
|
||||||
|
|
||||||
lazy_static! {
|
lazy_static! {
|
||||||
// Set once setup_tray_menu executed.
|
static ref SPN_STATUS: Mutex<Option<MenuItem<Wry>>> = Mutex::new(None);
|
||||||
static ref SPN_BUTTON: Mutex<Option<CheckMenuItem<Wry>>> = Mutex::new(None);
|
static ref SPN_BUTTON: Mutex<Option<MenuItem<Wry>>> = Mutex::new(None);
|
||||||
|
static ref GLOBAL_STATUS: Mutex<Option<MenuItem<Wry>>> = Mutex::new(None);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const PM_TRAY_ICON_ID: &'static str = "pm_icon";
|
||||||
|
|
||||||
// Icons
|
// Icons
|
||||||
//
|
//
|
||||||
const BLUE_ICON: &'static [u8] =
|
const BLUE_ICON: &'static [u8] = include_bytes!("../../../../assets/data/icons/pm_light_blue.ico");
|
||||||
include_bytes!("../../assets/icons/pm_light_blue_512.ico");
|
const RED_ICON: &'static [u8] = include_bytes!("../../../../assets/data/icons/pm_light_red.ico");
|
||||||
const RED_ICON: &'static [u8] =
|
|
||||||
include_bytes!("../../assets/icons/pm_light_red_512.ico");
|
|
||||||
const YELLOW_ICON: &'static [u8] =
|
const YELLOW_ICON: &'static [u8] =
|
||||||
include_bytes!("../../assets/icons/pm_light_yellow_512.ico");
|
include_bytes!("../../../../assets/data/icons/pm_light_yellow.ico");
|
||||||
const GREEN_ICON: &'static [u8] =
|
const GREEN_ICON: &'static [u8] =
|
||||||
include_bytes!("../../assets/icons/pm_light_green_512.ico");
|
include_bytes!("../../../../assets/data/icons/pm_light_green.ico");
|
||||||
|
|
||||||
pub fn setup_tray_menu(
|
pub fn setup_tray_menu(
|
||||||
app: &mut tauri::App,
|
app: &mut tauri::App,
|
||||||
) -> core::result::Result<AppIcon, Box<dyn std::error::Error>> {
|
) -> core::result::Result<AppIcon, Box<dyn std::error::Error>> {
|
||||||
// Tray menu
|
// Tray menu
|
||||||
let close_btn = MenuItemBuilder::with_id("close", "Exit").build(app);
|
let open_btn = MenuItemBuilder::with_id("open", "Open App").build(app)?;
|
||||||
let open_btn = MenuItemBuilder::with_id("open", "Open").build(app);
|
let exit_ui_btn = MenuItemBuilder::with_id("exit_ui", "Exit UI").build(app)?;
|
||||||
|
let shutdown_btn = MenuItemBuilder::with_id("shutdown", "Shut Down Portmaster").build(app)?;
|
||||||
|
|
||||||
let spn = CheckMenuItemBuilder::with_id("spn", "Use SPN").build(app);
|
let global_status = MenuItemBuilder::with_id("global_status", "Status: Secured")
|
||||||
|
.enabled(false)
|
||||||
|
.build(app)
|
||||||
|
.unwrap();
|
||||||
|
{
|
||||||
|
let mut button_ref = GLOBAL_STATUS.lock()?;
|
||||||
|
*button_ref = Some(global_status.clone());
|
||||||
|
}
|
||||||
|
|
||||||
// Store the SPN button reference
|
// Setup SPN status
|
||||||
let mut button_ref = SPN_BUTTON.lock().unwrap();
|
let spn_status = MenuItemBuilder::with_id("spn_status", "SPN: Disabled")
|
||||||
|
.enabled(false)
|
||||||
|
.build(app)
|
||||||
|
.unwrap();
|
||||||
|
{
|
||||||
|
let mut button_ref = SPN_STATUS.lock()?;
|
||||||
|
*button_ref = Some(spn_status.clone());
|
||||||
|
}
|
||||||
|
// Setup SPN button
|
||||||
|
let spn = MenuItemBuilder::with_id("spn_toggle", "Enable SPN")
|
||||||
|
.build(app)
|
||||||
|
.unwrap();
|
||||||
|
{
|
||||||
|
let mut button_ref = SPN_BUTTON.lock()?;
|
||||||
*button_ref = Some(spn.clone());
|
*button_ref = Some(spn.clone());
|
||||||
|
}
|
||||||
|
|
||||||
let force_show_window = MenuItemBuilder::with_id("force-show", "Force Show UI").build(app);
|
let force_show_window = MenuItemBuilder::with_id("force-show", "Force Show UI").build(app)?;
|
||||||
let reload_btn = MenuItemBuilder::with_id("reload", "Reload User Interface").build(app);
|
let reload_btn = MenuItemBuilder::with_id("reload", "Reload User Interface").build(app)?;
|
||||||
let developer_menu = SubmenuBuilder::new(app, "Developer")
|
let developer_menu = SubmenuBuilder::new(app, "Developer")
|
||||||
.items(&[&reload_btn, &force_show_window])
|
.items(&[&reload_btn, &force_show_window])
|
||||||
.build()?;
|
.build()?;
|
||||||
|
|
||||||
// Drop the reference now so we unlock immediately.
|
|
||||||
drop(button_ref);
|
|
||||||
|
|
||||||
let menu = MenuBuilder::new(app)
|
let menu = MenuBuilder::new(app)
|
||||||
.items(&[
|
.items(&[
|
||||||
&spn,
|
|
||||||
&PredefinedMenuItem::separator(app),
|
|
||||||
&open_btn,
|
&open_btn,
|
||||||
&close_btn,
|
&PredefinedMenuItem::separator(app)?,
|
||||||
|
&global_status,
|
||||||
|
&PredefinedMenuItem::separator(app)?,
|
||||||
|
&spn_status,
|
||||||
|
&spn,
|
||||||
|
&PredefinedMenuItem::separator(app)?,
|
||||||
|
&exit_ui_btn,
|
||||||
|
&shutdown_btn,
|
||||||
&developer_menu,
|
&developer_menu,
|
||||||
])
|
])
|
||||||
.build()?;
|
.build()?;
|
||||||
|
|
||||||
let icon = TrayIconBuilder::new()
|
let icon = TrayIconBuilder::with_id(PM_TRAY_ICON_ID)
|
||||||
.icon(Icon::Raw(RED_ICON.to_vec()))
|
.icon(Image::from_bytes(RED_ICON).unwrap())
|
||||||
.menu(&menu)
|
.menu(&menu)
|
||||||
.on_menu_event(move |app, event| match event.id().as_ref() {
|
.on_menu_event(move |app, event| match event.id().as_ref() {
|
||||||
"close" => {
|
"exit_ui" => {
|
||||||
let handle = app.clone();
|
let handle = app.clone();
|
||||||
app.dialog()
|
app.dialog()
|
||||||
.message("This does not stop the Portmaster system service")
|
.message("This does not stop the Portmaster system service")
|
||||||
|
@ -90,7 +117,7 @@ pub fn setup_tray_menu(
|
||||||
.cancel_button_label("No")
|
.cancel_button_label("No")
|
||||||
.show(move |answer| {
|
.show(move |answer| {
|
||||||
if answer {
|
if answer {
|
||||||
let _ = handle.emit("exit-requested", "");
|
// let _ = handle.emit("exit-requested", "");
|
||||||
handle.exit(0);
|
handle.exit(0);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
@ -116,14 +143,15 @@ pub fn setup_tray_menu(
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
"spn" => {
|
"spn_toggle" => {
|
||||||
let btn = SPN_BUTTON.lock().unwrap();
|
if SPN_STATE.load(Ordering::Acquire) {
|
||||||
|
app.portmaster().set_spn_enabled(false);
|
||||||
if let Some(bt) = &*btn {
|
} else {
|
||||||
if let Ok(is_checked) = bt.is_checked() {
|
app.portmaster().set_spn_enabled(true);
|
||||||
app.portmaster().set_spn_enabled(is_checked);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
"shutdown" => {
|
||||||
|
app.portmaster().trigger_shutdown();
|
||||||
}
|
}
|
||||||
other => {
|
other => {
|
||||||
error!("unknown menu event id: {}", other);
|
error!("unknown menu event id: {}", other);
|
||||||
|
@ -131,12 +159,23 @@ pub fn setup_tray_menu(
|
||||||
})
|
})
|
||||||
.on_tray_icon_event(|tray, event| {
|
.on_tray_icon_event(|tray, event| {
|
||||||
// not supported on linux
|
// not supported on linux
|
||||||
if event.click_type == ClickType::Left {
|
|
||||||
|
if let tauri::tray::TrayIconEvent::Click {
|
||||||
|
id: _,
|
||||||
|
position: _,
|
||||||
|
rect: _,
|
||||||
|
button,
|
||||||
|
button_state,
|
||||||
|
} = event
|
||||||
|
{
|
||||||
|
if let MouseButton::Left = button {
|
||||||
|
if let MouseButtonState::Down = button_state {
|
||||||
let _ = open_window(tray.app_handle());
|
let _ = open_window(tray.app_handle());
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
})
|
})
|
||||||
.build(app)?;
|
.build(app)?;
|
||||||
|
|
||||||
Ok(icon)
|
Ok(icon)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -145,19 +184,27 @@ pub fn update_icon(icon: AppIcon, subsystems: HashMap<String, Subsystem>, spn_st
|
||||||
let failure = subsystems
|
let failure = subsystems
|
||||||
.values()
|
.values()
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.map(|s| s.failure_status)
|
.map(|s| &s.module_status)
|
||||||
.fold(
|
.fold((subsystem::FAILURE_NONE, "".to_string()), |mut acc, s| {
|
||||||
subsystem::FAILURE_NONE,
|
for m in s {
|
||||||
|acc, s| {
|
if m.failure_status > acc.0 {
|
||||||
if s > acc {
|
acc = (m.failure_status, m.failure_msg.clone())
|
||||||
s
|
|
||||||
} else {
|
|
||||||
acc
|
|
||||||
}
|
}
|
||||||
},
|
}
|
||||||
);
|
acc
|
||||||
|
});
|
||||||
|
|
||||||
let next_icon = match failure {
|
if failure.0 == subsystem::FAILURE_NONE {
|
||||||
|
if let Some(global_status) = &mut *(GLOBAL_STATUS.lock().unwrap()) {
|
||||||
|
_ = global_status.set_text("Status: Secured");
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if let Some(global_status) = &mut *(GLOBAL_STATUS.lock().unwrap()) {
|
||||||
|
_ = global_status.set_text(format!("Status: {}", failure.1));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let next_icon = match failure.0 {
|
||||||
subsystem::FAILURE_WARNING => YELLOW_ICON,
|
subsystem::FAILURE_WARNING => YELLOW_ICON,
|
||||||
subsystem::FAILURE_ERROR => RED_ICON,
|
subsystem::FAILURE_ERROR => RED_ICON,
|
||||||
_ => match spn_status.as_str() {
|
_ => match spn_status.as_str() {
|
||||||
|
@ -166,11 +213,11 @@ pub fn update_icon(icon: AppIcon, subsystems: HashMap<String, Subsystem>, spn_st
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
_ = icon.set_icon(Some(Icon::Raw(next_icon.to_vec())));
|
_ = icon.set_icon(Some(Image::from_bytes(next_icon).unwrap()));
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn tray_handler(cli: PortAPI, app: tauri::AppHandle) {
|
pub async fn tray_handler(cli: PortAPI, app: tauri::AppHandle) {
|
||||||
let icon = match app.tray() {
|
let icon = match app.tray_by_id(PM_TRAY_ICON_ID) {
|
||||||
Some(icon) => icon,
|
Some(icon) => icon,
|
||||||
None => {
|
None => {
|
||||||
error!("cancel try_handler: missing try icon");
|
error!("cancel try_handler: missing try icon");
|
||||||
|
@ -226,7 +273,23 @@ pub async fn tray_handler(cli: PortAPI, app: tauri::AppHandle) {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
_ = icon.set_icon(Some(Icon::Raw(BLUE_ICON.to_vec())));
|
let mut portmaster_shutdown_event_subscription = match cli
|
||||||
|
.request(Request::Subscribe(
|
||||||
|
"query runtime:modules/core/event/shutdown".to_string(),
|
||||||
|
))
|
||||||
|
.await
|
||||||
|
{
|
||||||
|
Ok(rx) => rx,
|
||||||
|
Err(err) => {
|
||||||
|
error!(
|
||||||
|
"cancel try_handler: failed to subscribe to 'runtime:modules/core/event/shutdown': {}",
|
||||||
|
err
|
||||||
|
);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
_ = icon.set_icon(Some(Image::from_bytes(BLUE_ICON).unwrap()));
|
||||||
|
|
||||||
let mut subsystems: HashMap<String, Subsystem> = HashMap::new();
|
let mut subsystems: HashMap<String, Subsystem> = HashMap::new();
|
||||||
let mut spn_status: String = "".to_string();
|
let mut spn_status: String = "".to_string();
|
||||||
|
@ -312,15 +375,7 @@ pub async fn tray_handler(cli: PortAPI, app: tauri::AppHandle) {
|
||||||
if let Some((_, payload)) = res {
|
if let Some((_, payload)) = res {
|
||||||
match payload.parse::<BooleanValue>() {
|
match payload.parse::<BooleanValue>() {
|
||||||
Ok(value) => {
|
Ok(value) => {
|
||||||
let mut btn = SPN_BUTTON.lock().unwrap();
|
update_spn_ui_state(value.value.unwrap_or(false));
|
||||||
|
|
||||||
if let Some(btn) = &mut *btn {
|
|
||||||
if let Some(value) = value.value {
|
|
||||||
_ = btn.set_checked(value);
|
|
||||||
} else {
|
|
||||||
_ = btn.set_checked(false);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
Err(err) => match err {
|
Err(err) => match err {
|
||||||
ParseError::JSON(err) => {
|
ParseError::JSON(err) => {
|
||||||
|
@ -332,13 +387,40 @@ pub async fn tray_handler(cli: PortAPI, app: tauri::AppHandle) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
msg = portmaster_shutdown_event_subscription.recv() => {
|
||||||
|
let msg = match msg {
|
||||||
|
Some(m) => m,
|
||||||
|
None => { break }
|
||||||
|
};
|
||||||
|
debug!("Shutdown request received: {:?}", msg);
|
||||||
|
match msg {
|
||||||
|
Response::Ok(_, _) | Response::New(_, _) | Response::Update(_, _) => app.exit(0),
|
||||||
|
_ => {},
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Some(btn) = &mut *(SPN_BUTTON.lock().unwrap()) {
|
update_spn_ui_state(false);
|
||||||
_ = btn.set_checked(false);
|
_ = icon.set_icon(Some(Image::from_bytes(RED_ICON).unwrap()));
|
||||||
}
|
}
|
||||||
|
|
||||||
_ = icon.set_icon(Some(Icon::Raw(RED_ICON.to_vec())));
|
fn update_spn_ui_state(enabled: bool) {
|
||||||
|
let mut spn_status = SPN_STATUS.lock().unwrap();
|
||||||
|
let Some(spn_status_ref) = &mut *spn_status else {
|
||||||
|
return;
|
||||||
|
};
|
||||||
|
let mut spn_btn = SPN_BUTTON.lock().unwrap();
|
||||||
|
let Some(spn_btn_ref) = &mut *spn_btn else {
|
||||||
|
return;
|
||||||
|
};
|
||||||
|
if enabled {
|
||||||
|
_ = spn_status_ref.set_text("SPN: Connected");
|
||||||
|
_ = spn_btn_ref.set_text("Disable SPN");
|
||||||
|
} else {
|
||||||
|
_ = spn_status_ref.set_text("SPN: Disabled");
|
||||||
|
_ = spn_btn_ref.set_text("Enable SPN");
|
||||||
|
}
|
||||||
|
SPN_STATE.store(enabled, Ordering::SeqCst);
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,7 @@
|
||||||
use log::{debug, error};
|
use log::{debug, error};
|
||||||
use tauri::{AppHandle, Manager, Result, UserAttentionType, Window, WindowBuilder, WindowUrl};
|
use tauri::{
|
||||||
|
AppHandle, Manager, Result, UserAttentionType, WebviewUrl, WebviewWindow, WebviewWindowBuilder,
|
||||||
|
};
|
||||||
|
|
||||||
use crate::portmaster::PortmasterExt;
|
use crate::portmaster::PortmasterExt;
|
||||||
|
|
||||||
|
@ -11,15 +13,16 @@ use crate::portmaster::PortmasterExt;
|
||||||
/// if ::websocket::is_portapi_reachable returns true.
|
/// if ::websocket::is_portapi_reachable returns true.
|
||||||
///
|
///
|
||||||
/// Either the existing or the newly created window is returned.
|
/// Either the existing or the newly created window is returned.
|
||||||
pub fn create_main_window(app: &AppHandle) -> Result<Window> {
|
pub fn create_main_window(app: &AppHandle) -> Result<WebviewWindow> {
|
||||||
let mut window = if let Some(window) = app.get_window("main") {
|
let mut window = if let Some(window) = app.get_webview_window("main") {
|
||||||
debug!("[tauri] main window already created");
|
debug!("[tauri] main window already created");
|
||||||
|
|
||||||
window
|
window
|
||||||
} else {
|
} else {
|
||||||
debug!("[tauri] creating main window");
|
debug!("[tauri] creating main window");
|
||||||
|
|
||||||
let res = WindowBuilder::new(app, "main", WindowUrl::App("index.html".into()))
|
let res = WebviewWindowBuilder::new(app, "main", WebviewUrl::App("index.html".into()))
|
||||||
|
.title("Portmaster")
|
||||||
.visible(false)
|
.visible(false)
|
||||||
.build();
|
.build();
|
||||||
|
|
||||||
|
@ -54,12 +57,12 @@ pub fn create_main_window(app: &AppHandle) -> Result<Window> {
|
||||||
Ok(window)
|
Ok(window)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn create_splash_window(app: &AppHandle) -> Result<Window> {
|
pub fn create_splash_window(app: &AppHandle) -> Result<WebviewWindow> {
|
||||||
if let Some(window) = app.get_window("splash") {
|
if let Some(window) = app.get_webview_window("splash") {
|
||||||
let _ = window.show();
|
let _ = window.show();
|
||||||
Ok(window)
|
Ok(window)
|
||||||
} else {
|
} else {
|
||||||
let window = WindowBuilder::new(app, "splash", WindowUrl::App("index.html".into()))
|
let window = WebviewWindowBuilder::new(app, "splash", WebviewUrl::App("index.html".into()))
|
||||||
.center()
|
.center()
|
||||||
.closable(false)
|
.closable(false)
|
||||||
.focused(true)
|
.focused(true)
|
||||||
|
@ -76,8 +79,9 @@ pub fn create_splash_window(app: &AppHandle) -> Result<Window> {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn close_splash_window(app: &AppHandle) -> Result<()> {
|
pub fn close_splash_window(app: &AppHandle) -> Result<()> {
|
||||||
if let Some(window) = app.get_window("splash") {
|
if let Some(window) = app.get_webview_window("splash") {
|
||||||
return window.close();
|
let _ = window.hide();
|
||||||
|
return window.destroy();
|
||||||
}
|
}
|
||||||
return Err(tauri::Error::WindowNotFound);
|
return Err(tauri::Error::WindowNotFound);
|
||||||
}
|
}
|
||||||
|
@ -94,9 +98,9 @@ pub fn close_splash_window(app: &AppHandle) -> Result<()> {
|
||||||
///
|
///
|
||||||
/// If the Portmaster API is unreachable and there's no main window yet, we show the
|
/// If the Portmaster API is unreachable and there's no main window yet, we show the
|
||||||
/// splash-screen window.
|
/// splash-screen window.
|
||||||
pub fn open_window(app: &AppHandle) -> Result<Window> {
|
pub fn open_window(app: &AppHandle) -> Result<WebviewWindow> {
|
||||||
if app.portmaster().is_reachable() {
|
if app.portmaster().is_reachable() {
|
||||||
match app.get_window("main") {
|
match app.get_webview_window("main") {
|
||||||
Some(win) => {
|
Some(win) => {
|
||||||
app.portmaster().show_window();
|
app.portmaster().show_window();
|
||||||
|
|
||||||
|
@ -122,32 +126,41 @@ pub fn open_window(app: &AppHandle) -> Result<Window> {
|
||||||
/// In #[cfg(debug_assertions)] the TAURI_PM_URL environment variable will be used
|
/// In #[cfg(debug_assertions)] the TAURI_PM_URL environment variable will be used
|
||||||
/// if set.
|
/// if set.
|
||||||
/// Otherwise or in release builds, it will be navigated to http://127.0.0.1:817.
|
/// Otherwise or in release builds, it will be navigated to http://127.0.0.1:817.
|
||||||
pub fn may_navigate_to_ui(win: &mut Window, force: bool) {
|
pub fn may_navigate_to_ui(win: &mut WebviewWindow, force: bool) {
|
||||||
if !win.app_handle().portmaster().is_reachable() && !force {
|
if !win.app_handle().portmaster().is_reachable() && !force {
|
||||||
error!("[tauri] portmaster API is not reachable, not navigating");
|
error!("[tauri] portmaster API is not reachable, not navigating");
|
||||||
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
if force || win.label().eq("main") {
|
||||||
if force || cfg!(debug_assertions) || win.url().as_str() == "tauri://localhost" {
|
|
||||||
#[cfg(debug_assertions)]
|
#[cfg(debug_assertions)]
|
||||||
if let Ok(target_url) = std::env::var("TAURI_PM_URL") {
|
if let Ok(target_url) = std::env::var("TAURI_PM_URL") {
|
||||||
debug!("[tauri] navigating to {}", target_url);
|
debug!("[tauri] navigating to {}", target_url);
|
||||||
|
|
||||||
win.navigate(target_url.parse().unwrap());
|
_ = win.navigate(target_url.parse().unwrap());
|
||||||
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(debug_assertions)]
|
#[cfg(debug_assertions)]
|
||||||
{
|
{
|
||||||
|
// Only for dev build
|
||||||
|
// Allow connection to http://localhost:4200
|
||||||
|
let capabilities = include_str!("../capabilities/default.json")
|
||||||
|
.replace("http://localhost:817", "http://localhost:4200");
|
||||||
|
let _ = win.add_capability(capabilities);
|
||||||
debug!("[tauri] navigating to http://localhost:4200");
|
debug!("[tauri] navigating to http://localhost:4200");
|
||||||
win.navigate("http://localhost:4200".parse().unwrap());
|
_ = win.navigate("http://localhost:4200".parse().unwrap());
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(not(debug_assertions))]
|
#[cfg(not(debug_assertions))]
|
||||||
win.navigate("http://localhost:817".parse().unwrap());
|
{
|
||||||
|
_ = win.navigate("http://localhost:817".parse().unwrap());
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
error!("not navigating to user interface: current url: {}", win.url().as_str());
|
error!(
|
||||||
|
"not navigating to user interface: current url: {}",
|
||||||
|
win.url().unwrap().as_str()
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,116 +0,0 @@
|
||||||
{
|
|
||||||
"build": {
|
|
||||||
"beforeDevCommand": {
|
|
||||||
"script": "npm run tauri-dev",
|
|
||||||
"cwd": "../../angular",
|
|
||||||
"wait": false
|
|
||||||
},
|
|
||||||
"devPath": "http://localhost:4100",
|
|
||||||
"distDir": "../../angular/dist/tauri-builtin",
|
|
||||||
"withGlobalTauri": true
|
|
||||||
},
|
|
||||||
"package": {
|
|
||||||
"productName": "Portmaster",
|
|
||||||
"version": "0.1.0"
|
|
||||||
},
|
|
||||||
"plugins": {
|
|
||||||
"cli": {
|
|
||||||
"args": [
|
|
||||||
{
|
|
||||||
"short": "d",
|
|
||||||
"name": "data",
|
|
||||||
"description": "Path to the installation directory",
|
|
||||||
"takesValue": true
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"short": "b",
|
|
||||||
"name": "background",
|
|
||||||
"description": "Start in the background without opening a window"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "with-notifications",
|
|
||||||
"description": "Enable experimental notifications via Tauri. Replaces the notifier app."
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "with-prompts",
|
|
||||||
"description": "Enable experimental prompt support via Tauri. Replaces the notifier app."
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"tauri": {
|
|
||||||
"bundle": {
|
|
||||||
"active": true,
|
|
||||||
"category": "Utility",
|
|
||||||
"copyright": "Safing Limited Inc",
|
|
||||||
"deb": {
|
|
||||||
"depends": [
|
|
||||||
"libayatana-appindicator3"
|
|
||||||
],
|
|
||||||
"desktopTemplate": "../../../packaging/linux/portmaster.desktop",
|
|
||||||
"files": {
|
|
||||||
"/usr/lib/systemd/system/portmaster.service": "../../../packaging/linux/portmaster.service",
|
|
||||||
"/etc/xdg/autostart/portmaster.desktop": "../../../packaging/linux/portmaster-autostart.desktop",
|
|
||||||
"/var/": "../../../packaging/linux/var",
|
|
||||||
"../control/postinst": "../../../packaging/linux/debian/postinst",
|
|
||||||
"../control/postrm": "../../../packaging/linux/debian/postrm"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"externalBin": [
|
|
||||||
"binaries/portmaster-start",
|
|
||||||
"binaries/portmaster-core"
|
|
||||||
],
|
|
||||||
"icon": [
|
|
||||||
"../assets/icons/pm_dark_512.png",
|
|
||||||
"../assets/icons/pm_dark_512.ico",
|
|
||||||
"../assets/icons/pm_light_512.png",
|
|
||||||
"../assets/icons/pm_light_512.ico"
|
|
||||||
],
|
|
||||||
"identifier": "io.safing.portmaster",
|
|
||||||
"longDescription": "",
|
|
||||||
"macOS": {
|
|
||||||
"entitlements": null,
|
|
||||||
"exceptionDomain": "",
|
|
||||||
"frameworks": [],
|
|
||||||
"providerShortName": null,
|
|
||||||
"signingIdentity": null
|
|
||||||
},
|
|
||||||
"resources": [],
|
|
||||||
"shortDescription": "",
|
|
||||||
"targets": [
|
|
||||||
"deb",
|
|
||||||
"appimage",
|
|
||||||
"nsis",
|
|
||||||
"msi",
|
|
||||||
"app"
|
|
||||||
],
|
|
||||||
"windows": {
|
|
||||||
"certificateThumbprint": null,
|
|
||||||
"digestAlgorithm": "sha256",
|
|
||||||
"timestampUrl": ""
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"security": {
|
|
||||||
"csp": null,
|
|
||||||
"dangerousRemoteDomainIpcAccess": [
|
|
||||||
{
|
|
||||||
"windows": [
|
|
||||||
"main",
|
|
||||||
"prompt"
|
|
||||||
],
|
|
||||||
"plugins": [
|
|
||||||
"shell",
|
|
||||||
"os",
|
|
||||||
"clipboard-manager",
|
|
||||||
"event",
|
|
||||||
"window",
|
|
||||||
"cli",
|
|
||||||
"portmaster"
|
|
||||||
],
|
|
||||||
"domain": "localhost"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"windows": []
|
|
||||||
}
|
|
||||||
}
|
|
106
desktop/tauri/src-tauri/tauri.conf.json5
Normal file
|
@ -0,0 +1,106 @@
|
||||||
|
{
|
||||||
|
"build": {
|
||||||
|
"beforeDevCommand": {
|
||||||
|
"script": "npm run tauri-dev",
|
||||||
|
"cwd": "../../angular",
|
||||||
|
"wait": false
|
||||||
|
},
|
||||||
|
"frontendDist": "../../angular/dist/tauri-builtin",
|
||||||
|
"devUrl": "http://localhost:4100"
|
||||||
|
},
|
||||||
|
"plugins": {
|
||||||
|
"cli": {
|
||||||
|
"args": [
|
||||||
|
{
|
||||||
|
"short": "d",
|
||||||
|
"name": "data",
|
||||||
|
"description": "Path to the installation directory",
|
||||||
|
"takesValue": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"short": "b",
|
||||||
|
"name": "background",
|
||||||
|
"description": "Start in the background without opening a window"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "with-notifications",
|
||||||
|
"description": "Enable experimental notifications via Tauri. Replaces the notifier app."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "with-prompts",
|
||||||
|
"description": "Enable experimental prompt support via Tauri. Replaces the notifier app."
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"productName": "Portmaster",
|
||||||
|
"version": "0.1.0",
|
||||||
|
"identifier": "io.safing.portmaster", // this is added as a property to the shortcut on windows (ApplicationUserModelID). Used for notifications.
|
||||||
|
"app": {
|
||||||
|
"withGlobalTauri": true,
|
||||||
|
"security": {
|
||||||
|
"csp": null
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"bundle": {
|
||||||
|
"active": true,
|
||||||
|
"category": "Utility",
|
||||||
|
"copyright": "Safing Limited Inc",
|
||||||
|
"linux": {
|
||||||
|
"deb": {
|
||||||
|
"depends": [
|
||||||
|
"libayatana-appindicator3"
|
||||||
|
],
|
||||||
|
"desktopTemplate": "../../../packaging/linux/portmaster.desktop",
|
||||||
|
"files": {
|
||||||
|
"/usr/lib/systemd/system/portmaster.service": "../../../packaging/linux/portmaster.service",
|
||||||
|
"/etc/xdg/autostart/portmaster.desktop": "../../../packaging/linux/portmaster-autostart.desktop"
|
||||||
|
},
|
||||||
|
"postInstallScript": "../../../packaging/linux/postinst",
|
||||||
|
"postRemoveScript": "../../../packaging/linux/postrm"
|
||||||
|
},
|
||||||
|
"rpm": {
|
||||||
|
"depends": [
|
||||||
|
"libayatana-appindicator-gtk3"
|
||||||
|
],
|
||||||
|
"desktopTemplate": "../../../packaging/linux/portmaster.desktop",
|
||||||
|
"release": "1",
|
||||||
|
"files": {
|
||||||
|
"/usr/lib/systemd/system/portmaster.service": "../../../packaging/linux/portmaster.service",
|
||||||
|
"/etc/xdg/autostart/portmaster.desktop": "../../../packaging/linux/portmaster-autostart.desktop"
|
||||||
|
},
|
||||||
|
"postInstallScript": "../../../packaging/linux/postinst",
|
||||||
|
"postRemoveScript": "../../../packaging/linux/postrm"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"windows": {
|
||||||
|
"nsis": {
|
||||||
|
"installMode": "perMachine",
|
||||||
|
"installerHooks": "templates/nsis_install_hooks.nsh",
|
||||||
|
"installerIcon": "../../../assets/data/icons/pm_light.ico"
|
||||||
|
},
|
||||||
|
"wix": {
|
||||||
|
"fragmentPaths": [
|
||||||
|
"templates/service.wxs"
|
||||||
|
],
|
||||||
|
"template": "templates/main.wxs"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"externalBin": [
|
||||||
|
"binaries/portmaster-start",
|
||||||
|
"binaries/portmaster-core"
|
||||||
|
],
|
||||||
|
"targets": [
|
||||||
|
"deb",
|
||||||
|
"rpm",
|
||||||
|
"nsis",
|
||||||
|
"msi"
|
||||||
|
],
|
||||||
|
"icon": [
|
||||||
|
"../../../assets/data/icons/pm_dark_512.png",
|
||||||
|
"../../../assets/data/icons/pm_dark.ico",
|
||||||
|
"../../../assets/data/icons/pm_light_512.png",
|
||||||
|
"../../../assets/data/icons/pm_light.ico"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
354
desktop/tauri/src-tauri/templates/main.wxs
Normal file
|
@ -0,0 +1,354 @@
|
||||||
|
<?if $(sys.BUILDARCH)="x86"?>
|
||||||
|
<?define Win64 = "no" ?>
|
||||||
|
<?define PlatformProgramFilesFolder = "ProgramFilesFolder" ?>
|
||||||
|
<?elseif $(sys.BUILDARCH)="x64"?>
|
||||||
|
<?define Win64 = "yes" ?>
|
||||||
|
<?define PlatformProgramFilesFolder = "ProgramFiles64Folder" ?>
|
||||||
|
<?elseif $(sys.BUILDARCH)="arm64"?>
|
||||||
|
<?define Win64 = "yes" ?>
|
||||||
|
<?define PlatformProgramFilesFolder = "ProgramFiles64Folder" ?>
|
||||||
|
<?else?>
|
||||||
|
<?error Unsupported value of sys.BUILDARCH=$(sys.BUILDARCH)?>
|
||||||
|
<?endif?>
|
||||||
|
|
||||||
|
<Wix xmlns="http://schemas.microsoft.com/wix/2006/wi">
|
||||||
|
<Product
|
||||||
|
Id="*"
|
||||||
|
Name="{{product_name}}"
|
||||||
|
UpgradeCode="{{upgrade_code}}"
|
||||||
|
Language="!(loc.TauriLanguage)"
|
||||||
|
Manufacturer="{{manufacturer}}"
|
||||||
|
Version="{{version}}">
|
||||||
|
|
||||||
|
<Package Id="*"
|
||||||
|
Keywords="Installer"
|
||||||
|
InstallerVersion="450"
|
||||||
|
Languages="0"
|
||||||
|
Compressed="yes"
|
||||||
|
InstallScope="perMachine"
|
||||||
|
SummaryCodepage="!(loc.TauriCodepage)"/>
|
||||||
|
|
||||||
|
<!-- https://docs.microsoft.com/en-us/windows/win32/msi/reinstallmode -->
|
||||||
|
<!-- reinstall all files; rewrite all registry entries; reinstall all shortcuts -->
|
||||||
|
<Property Id="REINSTALLMODE" Value="amus" />
|
||||||
|
|
||||||
|
<!-- Auto launch app after installation, useful for passive mode which usually used in updates -->
|
||||||
|
<Property Id="AUTOLAUNCHAPP" Secure="yes" />
|
||||||
|
<!-- Property to forward cli args to the launched app to not lose those of the pre-update instance -->
|
||||||
|
<Property Id="LAUNCHAPPARGS" Secure="yes" />
|
||||||
|
|
||||||
|
{{#if allow_downgrades}}
|
||||||
|
<MajorUpgrade Schedule="afterInstallInitialize" AllowDowngrades="yes" />
|
||||||
|
{{else}}
|
||||||
|
<MajorUpgrade Schedule="afterInstallInitialize" DowngradeErrorMessage="!(loc.DowngradeErrorMessage)" AllowSameVersionUpgrades="yes" />
|
||||||
|
{{/if}}
|
||||||
|
|
||||||
|
<InstallExecuteSequence>
|
||||||
|
<RemoveShortcuts>Installed AND NOT UPGRADINGPRODUCTCODE</RemoveShortcuts>
|
||||||
|
</InstallExecuteSequence>
|
||||||
|
|
||||||
|
<Media Id="1" Cabinet="app.cab" EmbedCab="yes" />
|
||||||
|
|
||||||
|
{{#if banner_path}}
|
||||||
|
<WixVariable Id="WixUIBannerBmp" Value="{{banner_path}}" />
|
||||||
|
{{/if}}
|
||||||
|
{{#if dialog_image_path}}
|
||||||
|
<WixVariable Id="WixUIDialogBmp" Value="{{dialog_image_path}}" />
|
||||||
|
{{/if}}
|
||||||
|
{{#if license}}
|
||||||
|
<WixVariable Id="WixUILicenseRtf" Value="{{license}}" />
|
||||||
|
{{/if}}
|
||||||
|
|
||||||
|
<Icon Id="ProductIcon" SourceFile="{{icon_path}}"/>
|
||||||
|
<Property Id="ARPPRODUCTICON" Value="ProductIcon" />
|
||||||
|
<Property Id="ARPNOREPAIR" Value="yes" Secure="yes" /> <!-- Remove repair -->
|
||||||
|
<SetProperty Id="ARPNOMODIFY" Value="1" After="InstallValidate" Sequence="execute"/>
|
||||||
|
|
||||||
|
{{#if homepage}}
|
||||||
|
<Property Id="ARPURLINFOABOUT" Value="{{homepage}}"/>
|
||||||
|
<Property Id="ARPHELPLINK" Value="{{homepage}}"/>
|
||||||
|
<Property Id="ARPURLUPDATEINFO" Value="{{homepage}}"/>
|
||||||
|
{{/if}}
|
||||||
|
|
||||||
|
<!-- initialize with previous InstallDir -->
|
||||||
|
<Property Id="INSTALLDIR">
|
||||||
|
<RegistrySearch Id="PrevInstallDirReg" Root="HKCU" Key="Software\\{{manufacturer}}\\{{product_name}}" Name="InstallDir" Type="raw"/>
|
||||||
|
</Property>
|
||||||
|
|
||||||
|
<!-- launch app checkbox -->
|
||||||
|
<Property Id="WIXUI_EXITDIALOGOPTIONALCHECKBOXTEXT" Value="!(loc.LaunchApp)" />
|
||||||
|
<Property Id="WIXUI_EXITDIALOGOPTIONALCHECKBOX" Value="1"/>
|
||||||
|
<CustomAction Id="LaunchApplication" Impersonate="yes" FileKey="Path" ExeCommand="[LAUNCHAPPARGS]" Return="asyncNoWait" />
|
||||||
|
|
||||||
|
<UI>
|
||||||
|
<!-- launch app checkbox -->
|
||||||
|
<Publish Dialog="ExitDialog" Control="Finish" Event="DoAction" Value="LaunchApplication">WIXUI_EXITDIALOGOPTIONALCHECKBOX = 1 and NOT Installed</Publish>
|
||||||
|
|
||||||
|
<Property Id="WIXUI_INSTALLDIR" Value="INSTALLDIR" />
|
||||||
|
|
||||||
|
{{#unless license}}
|
||||||
|
<!-- Skip license dialog -->
|
||||||
|
<Publish Dialog="WelcomeDlg"
|
||||||
|
Control="Next"
|
||||||
|
Event="NewDialog"
|
||||||
|
Value="InstallDirDlg"
|
||||||
|
Order="2">1</Publish>
|
||||||
|
<Publish Dialog="InstallDirDlg"
|
||||||
|
Control="Back"
|
||||||
|
Event="NewDialog"
|
||||||
|
Value="WelcomeDlg"
|
||||||
|
Order="2">1</Publish>
|
||||||
|
{{/unless}}
|
||||||
|
</UI>
|
||||||
|
|
||||||
|
<UIRef Id="WixUI_InstallDir" />
|
||||||
|
|
||||||
|
<Directory Id="TARGETDIR" Name="SourceDir">
|
||||||
|
<Directory Id="DesktopFolder" Name="Desktop">
|
||||||
|
<Component Id="ApplicationShortcutDesktop" Guid="*">
|
||||||
|
<Shortcut Id="ApplicationDesktopShortcut" Name="{{product_name}}" Description="Runs {{product_name}}" Target="[!Path]" WorkingDirectory="INSTALLDIR" />
|
||||||
|
<RemoveFolder Id="DesktopFolder" On="uninstall" />
|
||||||
|
<RegistryValue Root="HKCU" Key="Software\\{{manufacturer}}\\{{product_name}}" Name="Desktop Shortcut" Type="integer" Value="1" KeyPath="yes" />
|
||||||
|
</Component>
|
||||||
|
</Directory>
|
||||||
|
<Directory Id="$(var.PlatformProgramFilesFolder)" Name="PFiles">
|
||||||
|
<Directory Id="INSTALLDIR" Name="{{product_name}}"/>
|
||||||
|
</Directory>
|
||||||
|
<Directory Id="ProgramMenuFolder">
|
||||||
|
<Directory Id="ApplicationProgramsFolder" Name="{{product_name}}"/>
|
||||||
|
</Directory>
|
||||||
|
</Directory>
|
||||||
|
|
||||||
|
<DirectoryRef Id="INSTALLDIR">
|
||||||
|
<Component Id="RegistryEntries" Guid="*">
|
||||||
|
<RegistryKey Root="HKCU" Key="Software\\{{manufacturer}}\\{{product_name}}">
|
||||||
|
<RegistryValue Name="InstallDir" Type="string" Value="[INSTALLDIR]" KeyPath="yes" />
|
||||||
|
</RegistryKey>
|
||||||
|
<!-- Change the Root to HKCU for perUser installations -->
|
||||||
|
{{#each deep_link_protocols as |protocol| ~}}
|
||||||
|
<RegistryKey Root="HKLM" Key="Software\Classes\\{{protocol}}">
|
||||||
|
<RegistryValue Type="string" Name="URL Protocol" Value=""/>
|
||||||
|
<RegistryValue Type="string" Value="URL:{{bundle_id}} protocol"/>
|
||||||
|
<RegistryKey Key="DefaultIcon">
|
||||||
|
<RegistryValue Type="string" Value=""[!Path]",0" />
|
||||||
|
</RegistryKey>
|
||||||
|
<RegistryKey Key="shell\open\command">
|
||||||
|
<RegistryValue Type="string" Value=""[!Path]" "%1"" />
|
||||||
|
</RegistryKey>
|
||||||
|
</RegistryKey>
|
||||||
|
{{/each~}}
|
||||||
|
</Component>
|
||||||
|
<Component Id="Path" Guid="{{path_component_guid}}" Win64="$(var.Win64)">
|
||||||
|
<File Id="Path" Source="{{app_exe_source}}" KeyPath="yes" Checksum="yes"/>
|
||||||
|
{{#each file_associations as |association| ~}}
|
||||||
|
{{#each association.ext as |ext| ~}}
|
||||||
|
<ProgId Id="{{../../product_name}}.{{ext}}" Advertise="yes" Description="{{association.description}}">
|
||||||
|
<Extension Id="{{ext}}" Advertise="yes">
|
||||||
|
<Verb Id="open" Command="Open with {{../../product_name}}" Argument=""%1"" />
|
||||||
|
</Extension>
|
||||||
|
</ProgId>
|
||||||
|
{{/each~}}
|
||||||
|
{{/each~}}
|
||||||
|
</Component>
|
||||||
|
{{#each binaries as |bin| ~}}
|
||||||
|
<Component Id="{{ bin.id }}" Guid="{{bin.guid}}" Win64="$(var.Win64)">
|
||||||
|
<File Id="Bin_{{ bin.id }}" Source="{{bin.path}}" KeyPath="yes"/>
|
||||||
|
</Component>
|
||||||
|
{{/each~}}
|
||||||
|
{{#if enable_elevated_update_task}}
|
||||||
|
<Component Id="UpdateTask" Guid="C492327D-9720-4CD5-8DB8-F09082AF44BE" Win64="$(var.Win64)">
|
||||||
|
<File Id="UpdateTask" Source="update.xml" KeyPath="yes" Checksum="yes"/>
|
||||||
|
</Component>
|
||||||
|
<Component Id="UpdateTaskInstaller" Guid="011F25ED-9BE3-50A7-9E9B-3519ED2B9932" Win64="$(var.Win64)">
|
||||||
|
<File Id="UpdateTaskInstaller" Source="install-task.ps1" KeyPath="yes" Checksum="yes"/>
|
||||||
|
</Component>
|
||||||
|
<Component Id="UpdateTaskUninstaller" Guid="D4F6CC3F-32DC-5FD0-95E8-782FFD7BBCE1" Win64="$(var.Win64)">
|
||||||
|
<File Id="UpdateTaskUninstaller" Source="uninstall-task.ps1" KeyPath="yes" Checksum="yes"/>
|
||||||
|
</Component>
|
||||||
|
{{/if}}
|
||||||
|
{{resources}}
|
||||||
|
<Component Id="CMP_UninstallShortcut" Guid="*">
|
||||||
|
|
||||||
|
<Shortcut Id="UninstallShortcut"
|
||||||
|
Name="Uninstall {{product_name}}"
|
||||||
|
Description="Uninstalls {{product_name}}"
|
||||||
|
Target="[System64Folder]msiexec.exe"
|
||||||
|
Arguments="/x [ProductCode]" />
|
||||||
|
|
||||||
|
<RemoveFolder Id="INSTALLDIR"
|
||||||
|
On="uninstall" />
|
||||||
|
|
||||||
|
<RegistryValue Root="HKCU"
|
||||||
|
Key="Software\\{{manufacturer}}\\{{product_name}}"
|
||||||
|
Name="Uninstaller Shortcut"
|
||||||
|
Type="integer"
|
||||||
|
Value="1"
|
||||||
|
KeyPath="yes" />
|
||||||
|
</Component>
|
||||||
|
</DirectoryRef>
|
||||||
|
|
||||||
|
<DirectoryRef Id="ApplicationProgramsFolder">
|
||||||
|
<Component Id="ApplicationShortcut" Guid="*">
|
||||||
|
<Shortcut Id="ApplicationStartMenuShortcut"
|
||||||
|
Name="{{product_name}}"
|
||||||
|
Description="Runs {{product_name}}"
|
||||||
|
Target="[!Path]"
|
||||||
|
Icon="ProductIcon"
|
||||||
|
WorkingDirectory="INSTALLDIR">
|
||||||
|
<ShortcutProperty Key="System.AppUserModel.ID" Value="{{bundle_id}}"/>
|
||||||
|
</Shortcut>
|
||||||
|
<RemoveFolder Id="ApplicationProgramsFolder" On="uninstall"/>
|
||||||
|
<RegistryValue Root="HKCU" Key="Software\\{{manufacturer}}\\{{product_name}}" Name="Start Menu Shortcut" Type="integer" Value="1" KeyPath="yes"/>
|
||||||
|
</Component>
|
||||||
|
</DirectoryRef>
|
||||||
|
|
||||||
|
{{#each merge_modules as |msm| ~}}
|
||||||
|
<DirectoryRef Id="TARGETDIR">
|
||||||
|
<Merge Id="{{ msm.name }}" SourceFile="{{ msm.path }}" DiskId="1" Language="!(loc.TauriLanguage)" />
|
||||||
|
</DirectoryRef>
|
||||||
|
|
||||||
|
<Feature Id="{{ msm.name }}" Title="{{ msm.name }}" AllowAdvertise="no" Display="hidden" Level="1">
|
||||||
|
<MergeRef Id="{{ msm.name }}"/>
|
||||||
|
</Feature>
|
||||||
|
{{/each~}}
|
||||||
|
|
||||||
|
<Feature
|
||||||
|
Id="MainProgram"
|
||||||
|
Title="Application"
|
||||||
|
Description="!(loc.InstallAppFeature)"
|
||||||
|
Level="1"
|
||||||
|
ConfigurableDirectory="INSTALLDIR"
|
||||||
|
AllowAdvertise="no"
|
||||||
|
Display="expand"
|
||||||
|
Absent="disallow">
|
||||||
|
|
||||||
|
<ComponentRef Id="RegistryEntries"/>
|
||||||
|
|
||||||
|
{{#each resource_file_ids as |resource_file_id| ~}}
|
||||||
|
<ComponentRef Id="{{ resource_file_id }}"/>
|
||||||
|
{{/each~}}
|
||||||
|
|
||||||
|
{{#if enable_elevated_update_task}}
|
||||||
|
<ComponentRef Id="UpdateTask" />
|
||||||
|
<ComponentRef Id="UpdateTaskInstaller" />
|
||||||
|
<ComponentRef Id="UpdateTaskUninstaller" />
|
||||||
|
{{/if}}
|
||||||
|
|
||||||
|
<Feature Id="ShortcutsFeature"
|
||||||
|
Title="Shortcuts"
|
||||||
|
Level="1">
|
||||||
|
<ComponentRef Id="Path"/>
|
||||||
|
<ComponentRef Id="CMP_UninstallShortcut" />
|
||||||
|
<ComponentRef Id="ApplicationShortcut" />
|
||||||
|
<ComponentRef Id="ApplicationShortcutDesktop" />
|
||||||
|
</Feature>
|
||||||
|
|
||||||
|
<Feature
|
||||||
|
Id="Environment"
|
||||||
|
Title="PATH Environment Variable"
|
||||||
|
Description="!(loc.PathEnvVarFeature)"
|
||||||
|
Level="1"
|
||||||
|
Absent="allow">
|
||||||
|
<ComponentRef Id="Path"/>
|
||||||
|
{{#each binaries as |bin| ~}}
|
||||||
|
<ComponentRef Id="{{ bin.id }}"/>
|
||||||
|
{{/each~}}
|
||||||
|
</Feature>
|
||||||
|
</Feature>
|
||||||
|
|
||||||
|
<Feature Id="External" AllowAdvertise="no" Absent="disallow">
|
||||||
|
{{#each component_group_refs as |id| ~}}
|
||||||
|
<ComponentGroupRef Id="{{ id }}"/>
|
||||||
|
{{/each~}}
|
||||||
|
{{#each component_refs as |id| ~}}
|
||||||
|
<ComponentRef Id="{{ id }}"/>
|
||||||
|
{{/each~}}
|
||||||
|
{{#each feature_group_refs as |id| ~}}
|
||||||
|
<FeatureGroupRef Id="{{ id }}"/>
|
||||||
|
{{/each~}}
|
||||||
|
{{#each feature_refs as |id| ~}}
|
||||||
|
<FeatureRef Id="{{ id }}"/>
|
||||||
|
{{/each~}}
|
||||||
|
{{#each merge_refs as |id| ~}}
|
||||||
|
<MergeRef Id="{{ id }}"/>
|
||||||
|
{{/each~}}
|
||||||
|
</Feature>
|
||||||
|
|
||||||
|
{{#if install_webview}}
|
||||||
|
<!-- WebView2 -->
|
||||||
|
<Property Id="WVRTINSTALLED">
|
||||||
|
<RegistrySearch Id="WVRTInstalledSystem" Root="HKLM" Key="SOFTWARE\Microsoft\EdgeUpdate\Clients\{F3017226-FE2A-4295-8BDF-00C3A9A7E4C5}" Name="pv" Type="raw" Win64="no" />
|
||||||
|
<RegistrySearch Id="WVRTInstalledUser" Root="HKCU" Key="SOFTWARE\Microsoft\EdgeUpdate\Clients\{F3017226-FE2A-4295-8BDF-00C3A9A7E4C5}" Name="pv" Type="raw"/>
|
||||||
|
</Property>
|
||||||
|
|
||||||
|
{{#if download_bootstrapper}}
|
||||||
|
<CustomAction Id='DownloadAndInvokeBootstrapper' Directory="INSTALLDIR" Execute="deferred" ExeCommand='powershell.exe -NoProfile -windowstyle hidden try [\{] [\[]Net.ServicePointManager[\]]::SecurityProtocol = [\[]Net.SecurityProtocolType[\]]::Tls12 [\}] catch [\{][\}]; Invoke-WebRequest -Uri "https://go.microsoft.com/fwlink/p/?LinkId=2124703" -OutFile "$env:TEMP\MicrosoftEdgeWebview2Setup.exe" ; Start-Process -FilePath "$env:TEMP\MicrosoftEdgeWebview2Setup.exe" -ArgumentList ({{webview_installer_args}} '/install') -Wait' Return='check'/>
|
||||||
|
<InstallExecuteSequence>
|
||||||
|
<Custom Action='DownloadAndInvokeBootstrapper' Before='InstallFinalize'>
|
||||||
|
<![CDATA[NOT(REMOVE OR WVRTINSTALLED)]]>
|
||||||
|
</Custom>
|
||||||
|
</InstallExecuteSequence>
|
||||||
|
{{/if}}
|
||||||
|
|
||||||
|
<!-- Embedded webview bootstrapper mode -->
|
||||||
|
{{#if webview2_bootstrapper_path}}
|
||||||
|
<Binary Id="MicrosoftEdgeWebview2Setup.exe" SourceFile="{{webview2_bootstrapper_path}}"/>
|
||||||
|
<CustomAction Id='InvokeBootstrapper' BinaryKey='MicrosoftEdgeWebview2Setup.exe' Execute="deferred" ExeCommand='{{webview_installer_args}} /install' Return='check' />
|
||||||
|
<InstallExecuteSequence>
|
||||||
|
<Custom Action='InvokeBootstrapper' Before='InstallFinalize'>
|
||||||
|
<![CDATA[NOT(REMOVE OR WVRTINSTALLED)]]>
|
||||||
|
</Custom>
|
||||||
|
</InstallExecuteSequence>
|
||||||
|
{{/if}}
|
||||||
|
|
||||||
|
<!-- Embedded offline installer -->
|
||||||
|
{{#if webview2_installer_path}}
|
||||||
|
<Binary Id="MicrosoftEdgeWebView2RuntimeInstaller.exe" SourceFile="{{webview2_installer_path}}"/>
|
||||||
|
<CustomAction Id='InvokeStandalone' BinaryKey='MicrosoftEdgeWebView2RuntimeInstaller.exe' Execute="deferred" ExeCommand='{{webview_installer_args}} /install' Return='check' />
|
||||||
|
<InstallExecuteSequence>
|
||||||
|
<Custom Action='InvokeStandalone' Before='InstallFinalize'>
|
||||||
|
<![CDATA[NOT(REMOVE OR WVRTINSTALLED)]]>
|
||||||
|
</Custom>
|
||||||
|
</InstallExecuteSequence>
|
||||||
|
{{/if}}
|
||||||
|
|
||||||
|
{{/if}}
|
||||||
|
|
||||||
|
{{#if enable_elevated_update_task}}
|
||||||
|
<!-- Install an elevated update task within Windows Task Scheduler -->
|
||||||
|
<CustomAction
|
||||||
|
Id="CreateUpdateTask"
|
||||||
|
Return="check"
|
||||||
|
Directory="INSTALLDIR"
|
||||||
|
Execute="commit"
|
||||||
|
Impersonate="yes"
|
||||||
|
ExeCommand="powershell.exe -WindowStyle hidden .\install-task.ps1" />
|
||||||
|
<InstallExecuteSequence>
|
||||||
|
<Custom Action='CreateUpdateTask' Before='InstallFinalize'>
|
||||||
|
NOT(REMOVE)
|
||||||
|
</Custom>
|
||||||
|
</InstallExecuteSequence>
|
||||||
|
<!-- Remove elevated update task during uninstall -->
|
||||||
|
<CustomAction
|
||||||
|
Id="DeleteUpdateTask"
|
||||||
|
Return="check"
|
||||||
|
Directory="INSTALLDIR"
|
||||||
|
ExeCommand="powershell.exe -WindowStyle hidden .\uninstall-task.ps1" />
|
||||||
|
<InstallExecuteSequence>
|
||||||
|
<Custom Action="DeleteUpdateTask" Before='InstallFinalize'>
|
||||||
|
(REMOVE = "ALL") AND NOT UPGRADINGPRODUCTCODE
|
||||||
|
</Custom>
|
||||||
|
</InstallExecuteSequence>
|
||||||
|
{{/if}}
|
||||||
|
|
||||||
|
<CustomActionRef Id='InstallPortmasterService' />
|
||||||
|
<CustomActionRef Id='StopPortmasterService' />
|
||||||
|
<CustomActionRef Id='DeletePortmasterService' />
|
||||||
|
|
||||||
|
<InstallExecuteSequence>
|
||||||
|
<Custom Action="LaunchApplication" After="InstallFinalize">AUTOLAUNCHAPP AND NOT Installed</Custom>
|
||||||
|
</InstallExecuteSequence>
|
||||||
|
|
||||||
|
<SetProperty Id="ARPINSTALLLOCATION" Value="[INSTALLDIR]" After="CostFinalize"/>
|
||||||
|
</Product>
|
||||||
|
</Wix>
|
350
desktop/tauri/src-tauri/templates/main_original.wxs
Normal file
|
@ -0,0 +1,350 @@
|
||||||
|
<?if $(sys.BUILDARCH)="x86"?>
|
||||||
|
<?define Win64 = "no" ?>
|
||||||
|
<?define PlatformProgramFilesFolder = "ProgramFilesFolder" ?>
|
||||||
|
<?elseif $(sys.BUILDARCH)="x64"?>
|
||||||
|
<?define Win64 = "yes" ?>
|
||||||
|
<?define PlatformProgramFilesFolder = "ProgramFiles64Folder" ?>
|
||||||
|
<?elseif $(sys.BUILDARCH)="arm64"?>
|
||||||
|
<?define Win64 = "yes" ?>
|
||||||
|
<?define PlatformProgramFilesFolder = "ProgramFiles64Folder" ?>
|
||||||
|
<?else?>
|
||||||
|
<?error Unsupported value of sys.BUILDARCH=$(sys.BUILDARCH)?>
|
||||||
|
<?endif?>
|
||||||
|
|
||||||
|
<Wix xmlns="http://schemas.microsoft.com/wix/2006/wi">
|
||||||
|
<Product
|
||||||
|
Id="*"
|
||||||
|
Name="{{product_name}}"
|
||||||
|
UpgradeCode="{{upgrade_code}}"
|
||||||
|
Language="!(loc.TauriLanguage)"
|
||||||
|
Manufacturer="{{manufacturer}}"
|
||||||
|
Version="{{version}}">
|
||||||
|
|
||||||
|
<Package Id="*"
|
||||||
|
Keywords="Installer"
|
||||||
|
InstallerVersion="450"
|
||||||
|
Languages="0"
|
||||||
|
Compressed="yes"
|
||||||
|
InstallScope="perMachine"
|
||||||
|
SummaryCodepage="!(loc.TauriCodepage)"/>
|
||||||
|
|
||||||
|
<!-- https://docs.microsoft.com/en-us/windows/win32/msi/reinstallmode -->
|
||||||
|
<!-- reinstall all files; rewrite all registry entries; reinstall all shortcuts -->
|
||||||
|
<Property Id="REINSTALLMODE" Value="amus" />
|
||||||
|
|
||||||
|
<!-- Auto launch app after installation, useful for passive mode which usually used in updates -->
|
||||||
|
<Property Id="AUTOLAUNCHAPP" Secure="yes" />
|
||||||
|
<!-- Property to forward cli args to the launched app to not lose those of the pre-update instance -->
|
||||||
|
<Property Id="LAUNCHAPPARGS" Secure="yes" />
|
||||||
|
|
||||||
|
{{#if allow_downgrades}}
|
||||||
|
<MajorUpgrade Schedule="afterInstallInitialize" AllowDowngrades="yes" />
|
||||||
|
{{else}}
|
||||||
|
<MajorUpgrade Schedule="afterInstallInitialize" DowngradeErrorMessage="!(loc.DowngradeErrorMessage)" AllowSameVersionUpgrades="yes" />
|
||||||
|
{{/if}}
|
||||||
|
|
||||||
|
<InstallExecuteSequence>
|
||||||
|
<RemoveShortcuts>Installed AND NOT UPGRADINGPRODUCTCODE</RemoveShortcuts>
|
||||||
|
</InstallExecuteSequence>
|
||||||
|
|
||||||
|
<Media Id="1" Cabinet="app.cab" EmbedCab="yes" />
|
||||||
|
|
||||||
|
{{#if banner_path}}
|
||||||
|
<WixVariable Id="WixUIBannerBmp" Value="{{banner_path}}" />
|
||||||
|
{{/if}}
|
||||||
|
{{#if dialog_image_path}}
|
||||||
|
<WixVariable Id="WixUIDialogBmp" Value="{{dialog_image_path}}" />
|
||||||
|
{{/if}}
|
||||||
|
{{#if license}}
|
||||||
|
<WixVariable Id="WixUILicenseRtf" Value="{{license}}" />
|
||||||
|
{{/if}}
|
||||||
|
|
||||||
|
<Icon Id="ProductIcon" SourceFile="{{icon_path}}"/>
|
||||||
|
<Property Id="ARPPRODUCTICON" Value="ProductIcon" />
|
||||||
|
<Property Id="ARPNOREPAIR" Value="yes" Secure="yes" /> <!-- Remove repair -->
|
||||||
|
<SetProperty Id="ARPNOMODIFY" Value="1" After="InstallValidate" Sequence="execute"/>
|
||||||
|
|
||||||
|
{{#if homepage}}
|
||||||
|
<Property Id="ARPURLINFOABOUT" Value="{{homepage}}"/>
|
||||||
|
<Property Id="ARPHELPLINK" Value="{{homepage}}"/>
|
||||||
|
<Property Id="ARPURLUPDATEINFO" Value="{{homepage}}"/>
|
||||||
|
{{/if}}
|
||||||
|
|
||||||
|
<!-- initialize with previous InstallDir -->
|
||||||
|
<Property Id="INSTALLDIR">
|
||||||
|
<RegistrySearch Id="PrevInstallDirReg" Root="HKCU" Key="Software\\{{manufacturer}}\\{{product_name}}" Name="InstallDir" Type="raw"/>
|
||||||
|
</Property>
|
||||||
|
|
||||||
|
<!-- launch app checkbox -->
|
||||||
|
<Property Id="WIXUI_EXITDIALOGOPTIONALCHECKBOXTEXT" Value="!(loc.LaunchApp)" />
|
||||||
|
<Property Id="WIXUI_EXITDIALOGOPTIONALCHECKBOX" Value="1"/>
|
||||||
|
<CustomAction Id="LaunchApplication" Impersonate="yes" FileKey="Path" ExeCommand="[LAUNCHAPPARGS]" Return="asyncNoWait" />
|
||||||
|
|
||||||
|
<UI>
|
||||||
|
<!-- launch app checkbox -->
|
||||||
|
<Publish Dialog="ExitDialog" Control="Finish" Event="DoAction" Value="LaunchApplication">WIXUI_EXITDIALOGOPTIONALCHECKBOX = 1 and NOT Installed</Publish>
|
||||||
|
|
||||||
|
<Property Id="WIXUI_INSTALLDIR" Value="INSTALLDIR" />
|
||||||
|
|
||||||
|
{{#unless license}}
|
||||||
|
<!-- Skip license dialog -->
|
||||||
|
<Publish Dialog="WelcomeDlg"
|
||||||
|
Control="Next"
|
||||||
|
Event="NewDialog"
|
||||||
|
Value="InstallDirDlg"
|
||||||
|
Order="2">1</Publish>
|
||||||
|
<Publish Dialog="InstallDirDlg"
|
||||||
|
Control="Back"
|
||||||
|
Event="NewDialog"
|
||||||
|
Value="WelcomeDlg"
|
||||||
|
Order="2">1</Publish>
|
||||||
|
{{/unless}}
|
||||||
|
</UI>
|
||||||
|
|
||||||
|
<UIRef Id="WixUI_InstallDir" />
|
||||||
|
|
||||||
|
<Directory Id="TARGETDIR" Name="SourceDir">
|
||||||
|
<Directory Id="DesktopFolder" Name="Desktop">
|
||||||
|
<Component Id="ApplicationShortcutDesktop" Guid="*">
|
||||||
|
<Shortcut Id="ApplicationDesktopShortcut" Name="{{product_name}}" Description="Runs {{product_name}}" Target="[!Path]" WorkingDirectory="INSTALLDIR" />
|
||||||
|
<RemoveFolder Id="DesktopFolder" On="uninstall" />
|
||||||
|
<RegistryValue Root="HKCU" Key="Software\\{{manufacturer}}\\{{product_name}}" Name="Desktop Shortcut" Type="integer" Value="1" KeyPath="yes" />
|
||||||
|
</Component>
|
||||||
|
</Directory>
|
||||||
|
<Directory Id="$(var.PlatformProgramFilesFolder)" Name="PFiles">
|
||||||
|
<Directory Id="INSTALLDIR" Name="{{product_name}}"/>
|
||||||
|
</Directory>
|
||||||
|
<Directory Id="ProgramMenuFolder">
|
||||||
|
<Directory Id="ApplicationProgramsFolder" Name="{{product_name}}"/>
|
||||||
|
</Directory>
|
||||||
|
</Directory>
|
||||||
|
|
||||||
|
<DirectoryRef Id="INSTALLDIR">
|
||||||
|
<Component Id="RegistryEntries" Guid="*">
|
||||||
|
<RegistryKey Root="HKCU" Key="Software\\{{manufacturer}}\\{{product_name}}">
|
||||||
|
<RegistryValue Name="InstallDir" Type="string" Value="[INSTALLDIR]" KeyPath="yes" />
|
||||||
|
</RegistryKey>
|
||||||
|
<!-- Change the Root to HKCU for perUser installations -->
|
||||||
|
{{#each deep_link_protocols as |protocol| ~}}
|
||||||
|
<RegistryKey Root="HKLM" Key="Software\Classes\\{{protocol}}">
|
||||||
|
<RegistryValue Type="string" Name="URL Protocol" Value=""/>
|
||||||
|
<RegistryValue Type="string" Value="URL:{{bundle_id}} protocol"/>
|
||||||
|
<RegistryKey Key="DefaultIcon">
|
||||||
|
<RegistryValue Type="string" Value=""[!Path]",0" />
|
||||||
|
</RegistryKey>
|
||||||
|
<RegistryKey Key="shell\open\command">
|
||||||
|
<RegistryValue Type="string" Value=""[!Path]" "%1"" />
|
||||||
|
</RegistryKey>
|
||||||
|
</RegistryKey>
|
||||||
|
{{/each~}}
|
||||||
|
</Component>
|
||||||
|
<Component Id="Path" Guid="{{path_component_guid}}" Win64="$(var.Win64)">
|
||||||
|
<File Id="Path" Source="{{app_exe_source}}" KeyPath="yes" Checksum="yes"/>
|
||||||
|
{{#each file_associations as |association| ~}}
|
||||||
|
{{#each association.ext as |ext| ~}}
|
||||||
|
<ProgId Id="{{../../product_name}}.{{ext}}" Advertise="yes" Description="{{association.description}}">
|
||||||
|
<Extension Id="{{ext}}" Advertise="yes">
|
||||||
|
<Verb Id="open" Command="Open with {{../../product_name}}" Argument=""%1"" />
|
||||||
|
</Extension>
|
||||||
|
</ProgId>
|
||||||
|
{{/each~}}
|
||||||
|
{{/each~}}
|
||||||
|
</Component>
|
||||||
|
{{#each binaries as |bin| ~}}
|
||||||
|
<Component Id="{{ bin.id }}" Guid="{{bin.guid}}" Win64="$(var.Win64)">
|
||||||
|
<File Id="Bin_{{ bin.id }}" Source="{{bin.path}}" KeyPath="yes"/>
|
||||||
|
</Component>
|
||||||
|
{{/each~}}
|
||||||
|
{{#if enable_elevated_update_task}}
|
||||||
|
<Component Id="UpdateTask" Guid="C492327D-9720-4CD5-8DB8-F09082AF44BE" Win64="$(var.Win64)">
|
||||||
|
<File Id="UpdateTask" Source="update.xml" KeyPath="yes" Checksum="yes"/>
|
||||||
|
</Component>
|
||||||
|
<Component Id="UpdateTaskInstaller" Guid="011F25ED-9BE3-50A7-9E9B-3519ED2B9932" Win64="$(var.Win64)">
|
||||||
|
<File Id="UpdateTaskInstaller" Source="install-task.ps1" KeyPath="yes" Checksum="yes"/>
|
||||||
|
</Component>
|
||||||
|
<Component Id="UpdateTaskUninstaller" Guid="D4F6CC3F-32DC-5FD0-95E8-782FFD7BBCE1" Win64="$(var.Win64)">
|
||||||
|
<File Id="UpdateTaskUninstaller" Source="uninstall-task.ps1" KeyPath="yes" Checksum="yes"/>
|
||||||
|
</Component>
|
||||||
|
{{/if}}
|
||||||
|
{{resources}}
|
||||||
|
<Component Id="CMP_UninstallShortcut" Guid="*">
|
||||||
|
|
||||||
|
<Shortcut Id="UninstallShortcut"
|
||||||
|
Name="Uninstall {{product_name}}"
|
||||||
|
Description="Uninstalls {{product_name}}"
|
||||||
|
Target="[System64Folder]msiexec.exe"
|
||||||
|
Arguments="/x [ProductCode]" />
|
||||||
|
|
||||||
|
<RemoveFolder Id="INSTALLDIR"
|
||||||
|
On="uninstall" />
|
||||||
|
|
||||||
|
<RegistryValue Root="HKCU"
|
||||||
|
Key="Software\\{{manufacturer}}\\{{product_name}}"
|
||||||
|
Name="Uninstaller Shortcut"
|
||||||
|
Type="integer"
|
||||||
|
Value="1"
|
||||||
|
KeyPath="yes" />
|
||||||
|
</Component>
|
||||||
|
</DirectoryRef>
|
||||||
|
|
||||||
|
<DirectoryRef Id="ApplicationProgramsFolder">
|
||||||
|
<Component Id="ApplicationShortcut" Guid="*">
|
||||||
|
<Shortcut Id="ApplicationStartMenuShortcut"
|
||||||
|
Name="{{product_name}}"
|
||||||
|
Description="Runs {{product_name}}"
|
||||||
|
Target="[!Path]"
|
||||||
|
Icon="ProductIcon"
|
||||||
|
WorkingDirectory="INSTALLDIR">
|
||||||
|
<ShortcutProperty Key="System.AppUserModel.ID" Value="{{bundle_id}}"/>
|
||||||
|
</Shortcut>
|
||||||
|
<RemoveFolder Id="ApplicationProgramsFolder" On="uninstall"/>
|
||||||
|
<RegistryValue Root="HKCU" Key="Software\\{{manufacturer}}\\{{product_name}}" Name="Start Menu Shortcut" Type="integer" Value="1" KeyPath="yes"/>
|
||||||
|
</Component>
|
||||||
|
</DirectoryRef>
|
||||||
|
|
||||||
|
{{#each merge_modules as |msm| ~}}
|
||||||
|
<DirectoryRef Id="TARGETDIR">
|
||||||
|
<Merge Id="{{ msm.name }}" SourceFile="{{ msm.path }}" DiskId="1" Language="!(loc.TauriLanguage)" />
|
||||||
|
</DirectoryRef>
|
||||||
|
|
||||||
|
<Feature Id="{{ msm.name }}" Title="{{ msm.name }}" AllowAdvertise="no" Display="hidden" Level="1">
|
||||||
|
<MergeRef Id="{{ msm.name }}"/>
|
||||||
|
</Feature>
|
||||||
|
{{/each~}}
|
||||||
|
|
||||||
|
<Feature
|
||||||
|
Id="MainProgram"
|
||||||
|
Title="Application"
|
||||||
|
Description="!(loc.InstallAppFeature)"
|
||||||
|
Level="1"
|
||||||
|
ConfigurableDirectory="INSTALLDIR"
|
||||||
|
AllowAdvertise="no"
|
||||||
|
Display="expand"
|
||||||
|
Absent="disallow">
|
||||||
|
|
||||||
|
<ComponentRef Id="RegistryEntries"/>
|
||||||
|
|
||||||
|
{{#each resource_file_ids as |resource_file_id| ~}}
|
||||||
|
<ComponentRef Id="{{ resource_file_id }}"/>
|
||||||
|
{{/each~}}
|
||||||
|
|
||||||
|
{{#if enable_elevated_update_task}}
|
||||||
|
<ComponentRef Id="UpdateTask" />
|
||||||
|
<ComponentRef Id="UpdateTaskInstaller" />
|
||||||
|
<ComponentRef Id="UpdateTaskUninstaller" />
|
||||||
|
{{/if}}
|
||||||
|
|
||||||
|
<Feature Id="ShortcutsFeature"
|
||||||
|
Title="Shortcuts"
|
||||||
|
Level="1">
|
||||||
|
<ComponentRef Id="Path"/>
|
||||||
|
<ComponentRef Id="CMP_UninstallShortcut" />
|
||||||
|
<ComponentRef Id="ApplicationShortcut" />
|
||||||
|
<ComponentRef Id="ApplicationShortcutDesktop" />
|
||||||
|
</Feature>
|
||||||
|
|
||||||
|
<Feature
|
||||||
|
Id="Environment"
|
||||||
|
Title="PATH Environment Variable"
|
||||||
|
Description="!(loc.PathEnvVarFeature)"
|
||||||
|
Level="1"
|
||||||
|
Absent="allow">
|
||||||
|
<ComponentRef Id="Path"/>
|
||||||
|
{{#each binaries as |bin| ~}}
|
||||||
|
<ComponentRef Id="{{ bin.id }}"/>
|
||||||
|
{{/each~}}
|
||||||
|
</Feature>
|
||||||
|
</Feature>
|
||||||
|
|
||||||
|
<Feature Id="External" AllowAdvertise="no" Absent="disallow">
|
||||||
|
{{#each component_group_refs as |id| ~}}
|
||||||
|
<ComponentGroupRef Id="{{ id }}"/>
|
||||||
|
{{/each~}}
|
||||||
|
{{#each component_refs as |id| ~}}
|
||||||
|
<ComponentRef Id="{{ id }}"/>
|
||||||
|
{{/each~}}
|
||||||
|
{{#each feature_group_refs as |id| ~}}
|
||||||
|
<FeatureGroupRef Id="{{ id }}"/>
|
||||||
|
{{/each~}}
|
||||||
|
{{#each feature_refs as |id| ~}}
|
||||||
|
<FeatureRef Id="{{ id }}"/>
|
||||||
|
{{/each~}}
|
||||||
|
{{#each merge_refs as |id| ~}}
|
||||||
|
<MergeRef Id="{{ id }}"/>
|
||||||
|
{{/each~}}
|
||||||
|
</Feature>
|
||||||
|
|
||||||
|
{{#if install_webview}}
|
||||||
|
<!-- WebView2 -->
|
||||||
|
<Property Id="WVRTINSTALLED">
|
||||||
|
<RegistrySearch Id="WVRTInstalledSystem" Root="HKLM" Key="SOFTWARE\Microsoft\EdgeUpdate\Clients\{F3017226-FE2A-4295-8BDF-00C3A9A7E4C5}" Name="pv" Type="raw" Win64="no" />
|
||||||
|
<RegistrySearch Id="WVRTInstalledUser" Root="HKCU" Key="SOFTWARE\Microsoft\EdgeUpdate\Clients\{F3017226-FE2A-4295-8BDF-00C3A9A7E4C5}" Name="pv" Type="raw"/>
|
||||||
|
</Property>
|
||||||
|
|
||||||
|
{{#if download_bootstrapper}}
|
||||||
|
<CustomAction Id='DownloadAndInvokeBootstrapper' Directory="INSTALLDIR" Execute="deferred" ExeCommand='powershell.exe -NoProfile -windowstyle hidden try [\{] [\[]Net.ServicePointManager[\]]::SecurityProtocol = [\[]Net.SecurityProtocolType[\]]::Tls12 [\}] catch [\{][\}]; Invoke-WebRequest -Uri "https://go.microsoft.com/fwlink/p/?LinkId=2124703" -OutFile "$env:TEMP\MicrosoftEdgeWebview2Setup.exe" ; Start-Process -FilePath "$env:TEMP\MicrosoftEdgeWebview2Setup.exe" -ArgumentList ({{webview_installer_args}} '/install') -Wait' Return='check'/>
|
||||||
|
<InstallExecuteSequence>
|
||||||
|
<Custom Action='DownloadAndInvokeBootstrapper' Before='InstallFinalize'>
|
||||||
|
<![CDATA[NOT(REMOVE OR WVRTINSTALLED)]]>
|
||||||
|
</Custom>
|
||||||
|
</InstallExecuteSequence>
|
||||||
|
{{/if}}
|
||||||
|
|
||||||
|
<!-- Embedded webview bootstrapper mode -->
|
||||||
|
{{#if webview2_bootstrapper_path}}
|
||||||
|
<Binary Id="MicrosoftEdgeWebview2Setup.exe" SourceFile="{{webview2_bootstrapper_path}}"/>
|
||||||
|
<CustomAction Id='InvokeBootstrapper' BinaryKey='MicrosoftEdgeWebview2Setup.exe' Execute="deferred" ExeCommand='{{webview_installer_args}} /install' Return='check' />
|
||||||
|
<InstallExecuteSequence>
|
||||||
|
<Custom Action='InvokeBootstrapper' Before='InstallFinalize'>
|
||||||
|
<![CDATA[NOT(REMOVE OR WVRTINSTALLED)]]>
|
||||||
|
</Custom>
|
||||||
|
</InstallExecuteSequence>
|
||||||
|
{{/if}}
|
||||||
|
|
||||||
|
<!-- Embedded offline installer -->
|
||||||
|
{{#if webview2_installer_path}}
|
||||||
|
<Binary Id="MicrosoftEdgeWebView2RuntimeInstaller.exe" SourceFile="{{webview2_installer_path}}"/>
|
||||||
|
<CustomAction Id='InvokeStandalone' BinaryKey='MicrosoftEdgeWebView2RuntimeInstaller.exe' Execute="deferred" ExeCommand='{{webview_installer_args}} /install' Return='check' />
|
||||||
|
<InstallExecuteSequence>
|
||||||
|
<Custom Action='InvokeStandalone' Before='InstallFinalize'>
|
||||||
|
<![CDATA[NOT(REMOVE OR WVRTINSTALLED)]]>
|
||||||
|
</Custom>
|
||||||
|
</InstallExecuteSequence>
|
||||||
|
{{/if}}
|
||||||
|
|
||||||
|
{{/if}}
|
||||||
|
|
||||||
|
{{#if enable_elevated_update_task}}
|
||||||
|
<!-- Install an elevated update task within Windows Task Scheduler -->
|
||||||
|
<CustomAction
|
||||||
|
Id="CreateUpdateTask"
|
||||||
|
Return="check"
|
||||||
|
Directory="INSTALLDIR"
|
||||||
|
Execute="commit"
|
||||||
|
Impersonate="yes"
|
||||||
|
ExeCommand="powershell.exe -WindowStyle hidden .\install-task.ps1" />
|
||||||
|
<InstallExecuteSequence>
|
||||||
|
<Custom Action='CreateUpdateTask' Before='InstallFinalize'>
|
||||||
|
NOT(REMOVE)
|
||||||
|
</Custom>
|
||||||
|
</InstallExecuteSequence>
|
||||||
|
<!-- Remove elevated update task during uninstall -->
|
||||||
|
<CustomAction
|
||||||
|
Id="DeleteUpdateTask"
|
||||||
|
Return="check"
|
||||||
|
Directory="INSTALLDIR"
|
||||||
|
ExeCommand="powershell.exe -WindowStyle hidden .\uninstall-task.ps1" />
|
||||||
|
<InstallExecuteSequence>
|
||||||
|
<Custom Action="DeleteUpdateTask" Before='InstallFinalize'>
|
||||||
|
(REMOVE = "ALL") AND NOT UPGRADINGPRODUCTCODE
|
||||||
|
</Custom>
|
||||||
|
</InstallExecuteSequence>
|
||||||
|
{{/if}}
|
||||||
|
|
||||||
|
<InstallExecuteSequence>
|
||||||
|
<Custom Action="LaunchApplication" After="InstallFinalize">AUTOLAUNCHAPP AND NOT Installed</Custom>
|
||||||
|
</InstallExecuteSequence>
|
||||||
|
|
||||||
|
<SetProperty Id="ARPINSTALLLOCATION" Value="[INSTALLDIR]" After="CostFinalize"/>
|
||||||
|
</Product>
|
||||||
|
</Wix>
|
14
desktop/tauri/src-tauri/templates/nsis_install_hooks.nsh
Normal file
|
@ -0,0 +1,14 @@
|
||||||
|
!define NSIS_HOOK_POSTINSTALL "NSIS_HOOK_POSTINSTALL_"
|
||||||
|
|
||||||
|
!macro NSIS_HOOK_POSTINSTALL_
|
||||||
|
ExecWait '"$INSTDIR\portmaster-start.exe" install core-service --data="$INSTDIR\data"'
|
||||||
|
!macroend
|
||||||
|
|
||||||
|
|
||||||
|
!define NSIS_HOOK_PREUNINSTALL "NSIS_HOOK_PREUNINSTALL_"
|
||||||
|
|
||||||
|
!macro NSIS_HOOK_PREUNINSTALL_
|
||||||
|
ExecWait 'sc.exe stop PortmasterCore'
|
||||||
|
ExecWait 'sc.exe delete PortmasterCore'
|
||||||
|
!macroend
|
||||||
|
|
37
desktop/tauri/src-tauri/templates/service.wxs
Normal file
|
@ -0,0 +1,37 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<Wix xmlns="http://schemas.microsoft.com/wix/2006/wi">
|
||||||
|
<Fragment>
|
||||||
|
<CustomAction Id="InstallPortmasterService"
|
||||||
|
Directory="INSTALLDIR"
|
||||||
|
ExeCommand=""[INSTALLDIR]portmaster-start.exe" install core-service --data="[INSTALLDIR]data""
|
||||||
|
Execute="commit"
|
||||||
|
Return="check"
|
||||||
|
Impersonate="no"
|
||||||
|
/>
|
||||||
|
<CustomAction Id="StopPortmasterService"
|
||||||
|
Directory="INSTALLDIR"
|
||||||
|
ExeCommand="sc.exe stop PortmasterCore"
|
||||||
|
Execute="commit"
|
||||||
|
Return="ignore"
|
||||||
|
Impersonate="no"
|
||||||
|
/>
|
||||||
|
<CustomAction Id="DeletePortmasterService"
|
||||||
|
Directory="INSTALLDIR"
|
||||||
|
ExeCommand="sc.exe delete PortmasterCore"
|
||||||
|
Execute="commit"
|
||||||
|
Return="ignore"
|
||||||
|
Impersonate="no"
|
||||||
|
/>
|
||||||
|
<InstallExecuteSequence>
|
||||||
|
<Custom Action="InstallPortmasterService" Before='InstallFinalize'>
|
||||||
|
<![CDATA[NOT Installed]]>
|
||||||
|
</Custom>
|
||||||
|
<Custom Action="StopPortmasterService" Before='InstallFinalize'>
|
||||||
|
REMOVE
|
||||||
|
</Custom>
|
||||||
|
<Custom Action="DeletePortmasterService" Before='InstallFinalize'>
|
||||||
|
REMOVE
|
||||||
|
</Custom>
|
||||||
|
</InstallExecuteSequence>
|
||||||
|
</Fragment>
|
||||||
|
</Wix>
|
|
@ -33,10 +33,9 @@ PrivateDevices=yes
|
||||||
AmbientCapabilities=cap_chown cap_kill cap_net_admin cap_net_bind_service cap_net_broadcast cap_net_raw cap_sys_module cap_sys_ptrace cap_dac_override cap_fowner cap_fsetid
|
AmbientCapabilities=cap_chown cap_kill cap_net_admin cap_net_bind_service cap_net_broadcast cap_net_raw cap_sys_module cap_sys_ptrace cap_dac_override cap_fowner cap_fsetid
|
||||||
CapabilityBoundingSet=cap_chown cap_kill cap_net_admin cap_net_bind_service cap_net_broadcast cap_net_raw cap_sys_module cap_sys_ptrace cap_dac_override cap_fowner cap_fsetid
|
CapabilityBoundingSet=cap_chown cap_kill cap_net_admin cap_net_bind_service cap_net_broadcast cap_net_raw cap_sys_module cap_sys_ptrace cap_dac_override cap_fowner cap_fsetid
|
||||||
StateDirectory=portmaster
|
StateDirectory=portmaster
|
||||||
ExecStartPre=-/usr/bin/portmaster-start --data $STATE_DIRECTORY clean-structure
|
|
||||||
# TODO(ppacher): add --disable-software-updates once it's merged and the release process changed.
|
# TODO(ppacher): add --disable-software-updates once it's merged and the release process changed.
|
||||||
ExecStart=/usr/bin/portmaster-core --data $STATE_DIRECTORY $PORTMASTER_ARGS
|
ExecStart=/usr/bin/portmaster-start --data /opt/safing/portmaster core -- $PORTMASTER_ARGS
|
||||||
ExecStartPost=-/usr/bin/portmaster-start recover-iptables
|
ExecStopPost=-/usr/bin/portmaster-start recover-iptables
|
||||||
|
|
||||||
[Install]
|
[Install]
|
||||||
WantedBy=multi-user.target
|
WantedBy=multi-user.target
|
||||||
|
|
|
@ -57,6 +57,7 @@ func MandatoryUpdates() (identifiers []string) {
|
||||||
PlatformIdentifier("start/portmaster-start.exe"),
|
PlatformIdentifier("start/portmaster-start.exe"),
|
||||||
PlatformIdentifier("notifier/portmaster-notifier.exe"),
|
PlatformIdentifier("notifier/portmaster-notifier.exe"),
|
||||||
PlatformIdentifier("notifier/portmaster-wintoast.dll"),
|
PlatformIdentifier("notifier/portmaster-wintoast.dll"),
|
||||||
|
PlatformIdentifier("app2/portmaster-app.zip"),
|
||||||
)
|
)
|
||||||
} else {
|
} else {
|
||||||
identifiers = append(
|
identifiers = append(
|
||||||
|
@ -64,6 +65,7 @@ func MandatoryUpdates() (identifiers []string) {
|
||||||
PlatformIdentifier("core/portmaster-core"),
|
PlatformIdentifier("core/portmaster-core"),
|
||||||
PlatformIdentifier("start/portmaster-start"),
|
PlatformIdentifier("start/portmaster-start"),
|
||||||
PlatformIdentifier("notifier/portmaster-notifier"),
|
PlatformIdentifier("notifier/portmaster-notifier"),
|
||||||
|
PlatformIdentifier("app2/portmaster-app"),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -88,5 +90,6 @@ func AutoUnpackUpdates() []string {
|
||||||
|
|
||||||
return []string{
|
return []string{
|
||||||
PlatformIdentifier("app/portmaster-app.zip"),
|
PlatformIdentifier("app/portmaster-app.zip"),
|
||||||
|
PlatformIdentifier("app2/portmaster-app.zip"),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|