mirror of
https://github.com/yuzu-emu/liftinstall.git
synced 2024-12-22 20:35:37 +00:00
Merge remote-tracking branch 'fix-usability' into yuzu
This commit is contained in:
commit
a816cbe767
53
.github/workflows/test-build.yml
vendored
Normal file
53
.github/workflows/test-build.yml
vendored
Normal file
|
@ -0,0 +1,53 @@
|
||||||
|
name: Rust
|
||||||
|
|
||||||
|
on: [push, pull_request]
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
build:
|
||||||
|
|
||||||
|
runs-on: ${{ matrix.os }}
|
||||||
|
strategy:
|
||||||
|
matrix:
|
||||||
|
os: [ubuntu-latest, windows-latest, macOS-latest]
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- uses: hecrj/setup-rust-action@master
|
||||||
|
with:
|
||||||
|
rust-version: stable
|
||||||
|
|
||||||
|
- name: Install dependencies
|
||||||
|
run: sudo apt-get update && sudo apt-get install -y libwebkit2gtk-4.0-dev libssl-dev
|
||||||
|
if: runner.os == 'Linux'
|
||||||
|
|
||||||
|
- name: Get yarn cache directory path
|
||||||
|
id: yarn-cache-dir-path
|
||||||
|
run: echo "::set-output name=dir::$(yarn cache dir)"
|
||||||
|
|
||||||
|
- name: Get cargo cache directory path
|
||||||
|
id: cargo-cache-dir-path
|
||||||
|
run: echo "::set-output name=dir::$HOME/.cargo/"
|
||||||
|
|
||||||
|
- uses: actions/cache@v2
|
||||||
|
id: yarn-cache
|
||||||
|
with:
|
||||||
|
path: ${{ steps.yarn-cache-dir-path.outputs.dir }}
|
||||||
|
key: ${{ runner.os }}-yarn-${{ hashFiles('**/yarn.lock') }}
|
||||||
|
restore-keys: |
|
||||||
|
${{ runner.os }}-yarn-
|
||||||
|
|
||||||
|
- uses: actions/cache@v2
|
||||||
|
id: cargo-cache
|
||||||
|
with:
|
||||||
|
path: ${{ steps.cargo-cache-dir-path.outputs.dir }}
|
||||||
|
key: ${{ runner.os }}-cargo-${{ hashFiles('Cargo.lock') }}
|
||||||
|
restore-keys: |
|
||||||
|
${{ runner.os }}-cargo-
|
||||||
|
|
||||||
|
- uses: actions/setup-node@v1
|
||||||
|
with:
|
||||||
|
node-version: '12.x'
|
||||||
|
- run: npm install -g yarn
|
||||||
|
|
||||||
|
- uses: actions/checkout@v2
|
||||||
|
- name: Build
|
||||||
|
run: cargo build --verbose
|
27
.travis.yml
27
.travis.yml
|
@ -1,27 +0,0 @@
|
||||||
matrix:
|
|
||||||
include:
|
|
||||||
- os: linux
|
|
||||||
language: cpp
|
|
||||||
sudo: required
|
|
||||||
dist: trusty
|
|
||||||
services: docker
|
|
||||||
install: docker pull rust:1
|
|
||||||
cache:
|
|
||||||
directories:
|
|
||||||
- $HOME/.cargo
|
|
||||||
- $TRAVIS_BUILD_DIR/ui/node_modules
|
|
||||||
script: docker run -v $HOME/.cargo:/root/.cargo -v $(pwd):/liftinstall rust:1 /bin/bash -ex /liftinstall/.travis/build.sh
|
|
||||||
|
|
||||||
- os: osx
|
|
||||||
language: rust
|
|
||||||
cache: cargo
|
|
||||||
osx_image: xcode10
|
|
||||||
script: brew install yarn && cargo build
|
|
||||||
|
|
||||||
- os: windows
|
|
||||||
language: rust
|
|
||||||
cache: cargo
|
|
||||||
script:
|
|
||||||
- choco install nodejs yarn
|
|
||||||
- export PATH="$PROGRAMFILES/nodejs/:$PROGRAMFILES (x86)/Yarn/bin/:$PATH"
|
|
||||||
- cargo build
|
|
2566
Cargo.lock
generated
2566
Cargo.lock
generated
File diff suppressed because it is too large
Load diff
56
Cargo.toml
56
Cargo.toml
|
@ -1,6 +1,7 @@
|
||||||
[package]
|
[package]
|
||||||
name = "liftinstall"
|
name = "liftinstall"
|
||||||
version = "0.1.0"
|
version = "0.2.0"
|
||||||
|
edition = "2018"
|
||||||
authors = ["James <jselby@jselby.net>"]
|
authors = ["James <jselby@jselby.net>"]
|
||||||
repository = "https://github.com/j-selby/liftinstall.git"
|
repository = "https://github.com/j-selby/liftinstall.git"
|
||||||
documentation = "https://liftinstall.jselby.net"
|
documentation = "https://liftinstall.jselby.net"
|
||||||
|
@ -8,35 +9,36 @@ description = "An adaptable installer for your application."
|
||||||
build = "build.rs"
|
build = "build.rs"
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
web-view = {git = "https://github.com/j-selby/web-view.git", rev = "752106e4637356cbdb39a0bf1113ea3ae8a14243"}
|
web-view = { version = "0.7", features = ["edge"] }
|
||||||
|
tinyfiledialogs = "3.8"
|
||||||
|
|
||||||
hyper = "0.11.27"
|
hyper = "0.11.27"
|
||||||
futures = "0.1.25"
|
futures = "0.1.29"
|
||||||
mime_guess = "1.8.6"
|
mime_guess = "2.0"
|
||||||
url = "1.7.2"
|
url = "2.2"
|
||||||
|
|
||||||
reqwest = "0.9.21"
|
reqwest = "0.9.22"
|
||||||
number_prefix = "0.3.0"
|
number_prefix = "0.4"
|
||||||
|
|
||||||
serde = "1.0.89"
|
serde = "1.0"
|
||||||
serde_derive = "1.0.89"
|
serde_derive = "1.0"
|
||||||
serde_json = "1.0.39"
|
serde_json = "1.0"
|
||||||
|
|
||||||
toml = "0.5.0"
|
toml = "0.5"
|
||||||
|
|
||||||
semver = {version = "0.9.0", features = ["serde"]}
|
semver = {version = "1.0", features = ["serde"]}
|
||||||
regex = "1.1.5"
|
regex = "1.4"
|
||||||
|
|
||||||
dirs = "1.0.5"
|
dirs = "3.0"
|
||||||
zip = "0.5.1"
|
zip = "0.5"
|
||||||
xz2 = "0.1.6"
|
xz2 = "0.1"
|
||||||
tar = "0.4"
|
tar = "0.4"
|
||||||
|
|
||||||
log = "0.4"
|
log = "0.4"
|
||||||
fern = "0.5"
|
fern = "0.6"
|
||||||
chrono = "0.4.6"
|
chrono = "0.4"
|
||||||
|
|
||||||
clap = "2.32.0"
|
clap = "2.33"
|
||||||
|
|
||||||
# used to open a link to the users default browser
|
# used to open a link to the users default browser
|
||||||
webbrowser = "0.5.2"
|
webbrowser = "0.5.2"
|
||||||
|
@ -46,19 +48,19 @@ jsonwebtoken = "6"
|
||||||
base64 = "0.10.1"
|
base64 = "0.10.1"
|
||||||
|
|
||||||
[build-dependencies]
|
[build-dependencies]
|
||||||
walkdir = "2.2.7"
|
walkdir = "2.3"
|
||||||
serde = "1.0.89"
|
serde = "1.0"
|
||||||
serde_derive = "1.0.89"
|
serde_derive = "1.0"
|
||||||
toml = "0.5.0"
|
toml = "0.5"
|
||||||
which = "2.0.1"
|
which = "4.0"
|
||||||
|
|
||||||
[target.'cfg(windows)'.dependencies]
|
[target.'cfg(windows)'.dependencies]
|
||||||
winapi = { version = "0.3", features = ["psapi", "winbase", "winioctl", "winnt"] }
|
winapi = { version = "0.3", features = ["psapi", "winbase", "winioctl", "winnt"] }
|
||||||
widestring = "0.4.0"
|
widestring = "0.4"
|
||||||
|
|
||||||
[target.'cfg(not(windows))'.dependencies]
|
[target.'cfg(not(windows))'.dependencies]
|
||||||
sysinfo = "0.8.2"
|
sysinfo = "0.18"
|
||||||
slug = "0.1.4"
|
slug = "0.1"
|
||||||
|
|
||||||
[target.'cfg(windows)'.build-dependencies]
|
[target.'cfg(windows)'.build-dependencies]
|
||||||
winres = "0.1"
|
winres = "0.1"
|
||||||
|
|
13
SECURITY.md
Normal file
13
SECURITY.md
Normal file
|
@ -0,0 +1,13 @@
|
||||||
|
# Security Policy
|
||||||
|
|
||||||
|
## Supported Versions
|
||||||
|
|
||||||
|
As `liftinstall` is a template for your project, no specific versioning is
|
||||||
|
provided at this time, though a rough version is given in the Cargo file.
|
||||||
|
|
||||||
|
Only the latest version from this file will be supported.
|
||||||
|
|
||||||
|
## Reporting a Vulnerability
|
||||||
|
|
||||||
|
For any specific security concerns/vulnerabilities, please email me directly
|
||||||
|
at security *at* jselby.net.
|
9
build.rs
9
build.rs
|
@ -46,6 +46,8 @@ fn handle_binary(config: &BaseAttributes) {
|
||||||
|
|
||||||
cc::Build::new()
|
cc::Build::new()
|
||||||
.cpp(true)
|
.cpp(true)
|
||||||
|
.define("_WIN32_WINNT", Some("0x0600"))
|
||||||
|
.define("WINVER", Some("0x0600"))
|
||||||
.file("src/native/interop.cpp")
|
.file("src/native/interop.cpp")
|
||||||
.compile("interop");
|
.compile("interop");
|
||||||
}
|
}
|
||||||
|
@ -102,7 +104,7 @@ fn main() {
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.wait()
|
.wait()
|
||||||
.expect("Unable to install Node.JS dependencies using Yarn");
|
.expect("Unable to install Node.JS dependencies using Yarn");
|
||||||
Command::new(&yarn_binary)
|
let return_code = Command::new(&yarn_binary)
|
||||||
.args(&[
|
.args(&[
|
||||||
"--cwd",
|
"--cwd",
|
||||||
ui_dir.to_str().expect("Unable to covert path"),
|
ui_dir.to_str().expect("Unable to covert path"),
|
||||||
|
@ -114,8 +116,7 @@ fn main() {
|
||||||
.to_str()
|
.to_str()
|
||||||
.expect("Unable to convert path"),
|
.expect("Unable to convert path"),
|
||||||
])
|
])
|
||||||
.spawn()
|
.status()
|
||||||
.unwrap()
|
|
||||||
.wait()
|
|
||||||
.expect("Unable to build frontend assets using Webpack");
|
.expect("Unable to build frontend assets using Webpack");
|
||||||
|
assert!(return_code.success());
|
||||||
}
|
}
|
||||||
|
|
|
@ -41,7 +41,7 @@ impl<'a> Archive<'a> for ZipArchive<'a> {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
func(i, Some(max), archive.sanitized_name(), &mut archive)?;
|
func(i, Some(max), archive.mangled_name(), &mut archive)?;
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
|
|
|
@ -7,8 +7,8 @@ use toml::de::Error as TomlError;
|
||||||
|
|
||||||
use serde_json::{self, Error as SerdeError};
|
use serde_json::{self, Error as SerdeError};
|
||||||
|
|
||||||
use sources::get_by_name;
|
use crate::sources::get_by_name;
|
||||||
use sources::types::Release;
|
use crate::sources::types::Release;
|
||||||
|
|
||||||
/// Description of the source of a package.
|
/// Description of the source of a package.
|
||||||
#[derive(Debug, Deserialize, Serialize, Clone)]
|
#[derive(Debug, Deserialize, Serialize, Clone)]
|
||||||
|
|
|
@ -4,8 +4,8 @@
|
||||||
|
|
||||||
use std::sync::{Arc, RwLock};
|
use std::sync::{Arc, RwLock};
|
||||||
|
|
||||||
use installer::InstallerFramework;
|
use crate::installer::InstallerFramework;
|
||||||
use logging::LoggingErrors;
|
use crate::logging::LoggingErrors;
|
||||||
|
|
||||||
pub mod rest;
|
pub mod rest;
|
||||||
mod ui;
|
mod ui;
|
||||||
|
|
|
@ -2,7 +2,8 @@
|
||||||
|
|
||||||
extern crate mime_guess;
|
extern crate mime_guess;
|
||||||
|
|
||||||
use self::mime_guess::{get_mime_type, octet_stream};
|
use self::mime_guess::from_ext;
|
||||||
|
use self::mime_guess::mime::APPLICATION_OCTET_STREAM;
|
||||||
|
|
||||||
macro_rules! include_files_as_assets {
|
macro_rules! include_files_as_assets {
|
||||||
( $target_match:expr, $( $file_name:expr ),* ) => {
|
( $target_match:expr, $( $file_name:expr ),* ) => {
|
||||||
|
@ -23,9 +24,9 @@ pub fn file_from_string(file_path: &str) -> Option<(String, &'static [u8])> {
|
||||||
Some(ext_ptr) => {
|
Some(ext_ptr) => {
|
||||||
let ext = &file_path[ext_ptr + 1..];
|
let ext = &file_path[ext_ptr + 1..];
|
||||||
|
|
||||||
get_mime_type(ext)
|
from_ext(ext).first_or_octet_stream()
|
||||||
}
|
}
|
||||||
None => octet_stream(),
|
None => APPLICATION_OCTET_STREAM,
|
||||||
};
|
};
|
||||||
|
|
||||||
let string_mime = guessed_mime.to_string();
|
let string_mime = guessed_mime.to_string();
|
||||||
|
@ -44,10 +45,11 @@ pub fn file_from_string(file_path: &str) -> Option<(String, &'static [u8])> {
|
||||||
"/fonts/roboto-v18-latin-regular.eot",
|
"/fonts/roboto-v18-latin-regular.eot",
|
||||||
"/fonts/roboto-v18-latin-regular.woff",
|
"/fonts/roboto-v18-latin-regular.woff",
|
||||||
"/fonts/roboto-v18-latin-regular.woff2",
|
"/fonts/roboto-v18-latin-regular.woff2",
|
||||||
|
"/fonts/materialdesignicons-webfont.eot",
|
||||||
|
"/fonts/materialdesignicons-webfont.woff",
|
||||||
|
"/fonts/materialdesignicons-webfont.woff2",
|
||||||
"/js/chunk-vendors.js",
|
"/js/chunk-vendors.js",
|
||||||
"/js/chunk-vendors.js.map",
|
"/js/app.js"
|
||||||
"/js/app.js",
|
|
||||||
"/js/app.js.map"
|
|
||||||
)?;
|
)?;
|
||||||
|
|
||||||
Some((string_mime, contents))
|
Some((string_mime, contents))
|
||||||
|
|
|
@ -2,11 +2,11 @@
|
||||||
//!
|
//!
|
||||||
//! Contains the over-arching server object + methods to manipulate it.
|
//! Contains the over-arching server object + methods to manipulate it.
|
||||||
|
|
||||||
use frontend::rest::services::WebService;
|
use crate::frontend::rest::services::WebService;
|
||||||
|
|
||||||
use installer::InstallerFramework;
|
use crate::installer::InstallerFramework;
|
||||||
|
|
||||||
use logging::LoggingErrors;
|
use crate::logging::LoggingErrors;
|
||||||
|
|
||||||
use hyper::server::Http;
|
use hyper::server::Http;
|
||||||
|
|
||||||
|
|
|
@ -2,27 +2,23 @@
|
||||||
//!
|
//!
|
||||||
//! The /api/attr call returns an executable script containing session variables.
|
//! The /api/attr call returns an executable script containing session variables.
|
||||||
|
|
||||||
use frontend::rest::services::default_future;
|
use crate::frontend::rest::services::default_future;
|
||||||
use frontend::rest::services::encapsulate_json;
|
use crate::frontend::rest::services::Future;
|
||||||
use frontend::rest::services::Future;
|
use crate::frontend::rest::services::Request;
|
||||||
use frontend::rest::services::Request;
|
use crate::frontend::rest::services::Response;
|
||||||
use frontend::rest::services::Response;
|
use crate::frontend::rest::services::WebService;
|
||||||
use frontend::rest::services::WebService;
|
|
||||||
|
|
||||||
use hyper::header::{ContentLength, ContentType};
|
use hyper::header::{ContentLength, ContentType};
|
||||||
|
|
||||||
use logging::LoggingErrors;
|
use crate::logging::LoggingErrors;
|
||||||
|
|
||||||
pub fn handle(service: &WebService, _req: Request) -> Future {
|
pub fn handle(service: &WebService, _req: Request) -> Future {
|
||||||
let framework = service.get_framework_read();
|
let framework = service.get_framework_read();
|
||||||
|
|
||||||
let file = encapsulate_json(
|
let file = framework
|
||||||
"base_attributes",
|
.base_attributes
|
||||||
&framework
|
.to_json_str()
|
||||||
.base_attributes
|
.log_expect("Failed to render JSON representation of config");
|
||||||
.to_json_str()
|
|
||||||
.log_expect("Failed to render JSON representation of config"),
|
|
||||||
);
|
|
||||||
|
|
||||||
default_future(
|
default_future(
|
||||||
Response::new()
|
Response::new()
|
||||||
|
|
|
@ -4,18 +4,18 @@
|
||||||
//!
|
//!
|
||||||
//! This endpoint should be usable directly from a <script> tag during loading.
|
//! This endpoint should be usable directly from a <script> tag during loading.
|
||||||
|
|
||||||
use frontend::rest::services::Future;
|
use crate::frontend::rest::services::Future;
|
||||||
use frontend::rest::services::Request;
|
use crate::frontend::rest::services::Request;
|
||||||
use frontend::rest::services::Response;
|
use crate::frontend::rest::services::Response;
|
||||||
use frontend::rest::services::WebService;
|
use crate::frontend::rest::services::WebService;
|
||||||
|
|
||||||
use hyper::header::{ContentLength, ContentType};
|
use hyper::header::{ContentLength, ContentType};
|
||||||
|
|
||||||
use logging::LoggingErrors;
|
use crate::logging::LoggingErrors;
|
||||||
|
|
||||||
use config::Config;
|
use crate::config::Config;
|
||||||
|
|
||||||
use http::build_async_client;
|
use crate::http::build_async_client;
|
||||||
|
|
||||||
use futures::stream::Stream;
|
use futures::stream::Stream;
|
||||||
use futures::Future as _;
|
use futures::Future as _;
|
||||||
|
|
|
@ -2,17 +2,17 @@
|
||||||
//!
|
//!
|
||||||
//! This call returns if dark mode is enabled on the system currently.
|
//! This call returns if dark mode is enabled on the system currently.
|
||||||
|
|
||||||
use frontend::rest::services::default_future;
|
use crate::frontend::rest::services::default_future;
|
||||||
use frontend::rest::services::Future;
|
use crate::frontend::rest::services::Future;
|
||||||
use frontend::rest::services::Request;
|
use crate::frontend::rest::services::Request;
|
||||||
use frontend::rest::services::Response;
|
use crate::frontend::rest::services::Response;
|
||||||
use frontend::rest::services::WebService;
|
use crate::frontend::rest::services::WebService;
|
||||||
|
|
||||||
use hyper::header::{ContentLength, ContentType};
|
use hyper::header::{ContentLength, ContentType};
|
||||||
|
|
||||||
use logging::LoggingErrors;
|
use crate::logging::LoggingErrors;
|
||||||
|
|
||||||
use native::is_dark_mode_active;
|
use crate::native::is_dark_mode_active;
|
||||||
|
|
||||||
pub fn handle(_service: &WebService, _req: Request) -> Future {
|
pub fn handle(_service: &WebService, _req: Request) -> Future {
|
||||||
let file = serde_json::to_string(&is_dark_mode_active())
|
let file = serde_json::to_string(&is_dark_mode_active())
|
||||||
|
|
|
@ -2,15 +2,15 @@
|
||||||
//!
|
//!
|
||||||
//! The /api/default-path returns the default path for the application to install into.
|
//! The /api/default-path returns the default path for the application to install into.
|
||||||
|
|
||||||
use frontend::rest::services::default_future;
|
use crate::frontend::rest::services::default_future;
|
||||||
use frontend::rest::services::Future;
|
use crate::frontend::rest::services::Future;
|
||||||
use frontend::rest::services::Request;
|
use crate::frontend::rest::services::Request;
|
||||||
use frontend::rest::services::Response;
|
use crate::frontend::rest::services::Response;
|
||||||
use frontend::rest::services::WebService;
|
use crate::frontend::rest::services::WebService;
|
||||||
|
|
||||||
use hyper::header::{ContentLength, ContentType};
|
use hyper::header::{ContentLength, ContentType};
|
||||||
|
|
||||||
use logging::LoggingErrors;
|
use crate::logging::LoggingErrors;
|
||||||
|
|
||||||
/// Struct used by serde to send a JSON payload to the client containing an optional value.
|
/// Struct used by serde to send a JSON payload to the client containing an optional value.
|
||||||
#[derive(Serialize)]
|
#[derive(Serialize)]
|
||||||
|
|
|
@ -2,15 +2,18 @@
|
||||||
//!
|
//!
|
||||||
//! The /api/exit closes down the application.
|
//! The /api/exit closes down the application.
|
||||||
|
|
||||||
use frontend::rest::services::Future as InternalFuture;
|
use crate::frontend::rest::services::default_future;
|
||||||
use frontend::rest::services::{default_future, Request, Response, WebService};
|
use crate::frontend::rest::services::Future;
|
||||||
|
use crate::frontend::rest::services::Request;
|
||||||
|
use crate::frontend::rest::services::Response;
|
||||||
|
use crate::frontend::rest::services::WebService;
|
||||||
|
|
||||||
use hyper::header::ContentType;
|
use hyper::header::ContentType;
|
||||||
use hyper::StatusCode;
|
use hyper::StatusCode;
|
||||||
|
|
||||||
use std::process::exit;
|
use std::process::exit;
|
||||||
|
|
||||||
pub fn handle(service: &WebService, _req: Request) -> InternalFuture {
|
pub fn handle(service: &WebService, _req: Request) -> Future {
|
||||||
match service.get_framework_write().shutdown() {
|
match service.get_framework_write().shutdown() {
|
||||||
Ok(_) => {
|
Ok(_) => {
|
||||||
exit(0);
|
exit(0);
|
||||||
|
|
|
@ -2,14 +2,14 @@
|
||||||
//!
|
//!
|
||||||
//! The /api/install call installs a set of packages dictated by a POST request.
|
//! The /api/install call installs a set of packages dictated by a POST request.
|
||||||
|
|
||||||
use frontend::rest::services::stream_progress;
|
use crate::frontend::rest::services::stream_progress;
|
||||||
use frontend::rest::services::Future;
|
use crate::frontend::rest::services::Future;
|
||||||
use frontend::rest::services::Request;
|
use crate::frontend::rest::services::Request;
|
||||||
use frontend::rest::services::WebService;
|
use crate::frontend::rest::services::WebService;
|
||||||
|
|
||||||
use logging::LoggingErrors;
|
use crate::logging::LoggingErrors;
|
||||||
|
|
||||||
use installer::InstallMessage;
|
use crate::installer::InstallMessage;
|
||||||
|
|
||||||
use futures::future::Future as _;
|
use futures::future::Future as _;
|
||||||
use futures::stream::Stream;
|
use futures::stream::Stream;
|
||||||
|
@ -23,11 +23,12 @@ pub fn handle(service: &WebService, req: Request) -> Future {
|
||||||
|
|
||||||
Box::new(req.body().concat2().map(move |b| {
|
Box::new(req.body().concat2().map(move |b| {
|
||||||
let results = form_urlencoded::parse(b.as_ref())
|
let results = form_urlencoded::parse(b.as_ref())
|
||||||
.into_owned()
|
.into_owned()
|
||||||
.collect::<HashMap<String, String>>();
|
.collect::<HashMap<String, String>>();
|
||||||
|
|
||||||
let mut to_install = Vec::new();
|
let mut to_install = Vec::new();
|
||||||
let mut path: Option<String> = None;
|
let mut path: Option<String> = None;
|
||||||
|
let mut force_install = false;
|
||||||
let mut install_desktop_shortcut= false;
|
let mut install_desktop_shortcut= false;
|
||||||
|
|
||||||
// Transform results into just an array of stuff to install
|
// Transform results into just an array of stuff to install
|
||||||
|
@ -38,6 +39,10 @@ pub fn handle(service: &WebService, req: Request) -> Future {
|
||||||
} else if key == "installDesktopShortcut" {
|
} else if key == "installDesktopShortcut" {
|
||||||
info!("Found installDesktopShortcut {:?}", value);
|
info!("Found installDesktopShortcut {:?}", value);
|
||||||
install_desktop_shortcut = value == "true";
|
install_desktop_shortcut = value == "true";
|
||||||
|
}
|
||||||
|
|
||||||
|
if key == "mode" && value == "force" {
|
||||||
|
force_install = true;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -60,7 +65,7 @@ pub fn handle(service: &WebService, req: Request) -> Future {
|
||||||
framework.set_install_dir(&path);
|
framework.set_install_dir(&path);
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Err(v) = framework.install(to_install, &sender, new_install, install_desktop_shortcut) {
|
if let Err(v) = framework.install(to_install, &sender, new_install, install_desktop_shortcut, force_install) {
|
||||||
error!("Install error occurred: {:?}", v);
|
error!("Install error occurred: {:?}", v);
|
||||||
if let Err(v) = sender.send(InstallMessage::Error(v)) {
|
if let Err(v) = sender.send(InstallMessage::Error(v)) {
|
||||||
error!("Failed to send install error: {:?}", v);
|
error!("Failed to send install error: {:?}", v);
|
||||||
|
|
|
@ -5,15 +5,15 @@
|
||||||
//!
|
//!
|
||||||
//! e.g. if the application is in maintenance mode
|
//! e.g. if the application is in maintenance mode
|
||||||
|
|
||||||
use frontend::rest::services::default_future;
|
use crate::frontend::rest::services::default_future;
|
||||||
use frontend::rest::services::Future;
|
use crate::frontend::rest::services::Future;
|
||||||
use frontend::rest::services::Request;
|
use crate::frontend::rest::services::Request;
|
||||||
use frontend::rest::services::Response;
|
use crate::frontend::rest::services::Response;
|
||||||
use frontend::rest::services::WebService;
|
use crate::frontend::rest::services::WebService;
|
||||||
|
|
||||||
use hyper::header::{ContentLength, ContentType};
|
use hyper::header::{ContentLength, ContentType};
|
||||||
|
|
||||||
use logging::LoggingErrors;
|
use crate::logging::LoggingErrors;
|
||||||
|
|
||||||
pub fn handle(service: &WebService, _req: Request) -> Future {
|
pub fn handle(service: &WebService, _req: Request) -> Future {
|
||||||
let framework = service.get_framework_read();
|
let framework = service.get_framework_read();
|
||||||
|
|
|
@ -4,12 +4,12 @@
|
||||||
|
|
||||||
use std::sync::{Arc, RwLock, RwLockReadGuard, RwLockWriteGuard};
|
use std::sync::{Arc, RwLock, RwLockReadGuard, RwLockWriteGuard};
|
||||||
|
|
||||||
use installer::{InstallMessage, InstallerFramework};
|
use crate::installer::{InstallMessage, InstallerFramework};
|
||||||
|
|
||||||
use hyper::server::Service;
|
use hyper::server::Service;
|
||||||
use hyper::{Method, StatusCode};
|
use hyper::{Method, StatusCode};
|
||||||
|
|
||||||
use logging::LoggingErrors;
|
use crate::logging::LoggingErrors;
|
||||||
|
|
||||||
use std::sync::mpsc::{channel, Sender};
|
use std::sync::mpsc::{channel, Sender};
|
||||||
|
|
||||||
|
@ -33,6 +33,8 @@ mod packages;
|
||||||
mod static_files;
|
mod static_files;
|
||||||
mod uninstall;
|
mod uninstall;
|
||||||
mod update_updater;
|
mod update_updater;
|
||||||
|
mod verify_path;
|
||||||
|
mod view_folder;
|
||||||
|
|
||||||
/// Expected incoming Request format from Hyper.
|
/// Expected incoming Request format from Hyper.
|
||||||
pub type Request = hyper::server::Request;
|
pub type Request = hyper::server::Request;
|
||||||
|
@ -136,14 +138,16 @@ impl Service for WebService {
|
||||||
(Method::Get, "/api/config") => config::handle(self, req),
|
(Method::Get, "/api/config") => config::handle(self, req),
|
||||||
(Method::Get, "/api/dark-mode") => dark_mode::handle(self, req),
|
(Method::Get, "/api/dark-mode") => dark_mode::handle(self, req),
|
||||||
(Method::Get, "/api/default-path") => default_path::handle(self, req),
|
(Method::Get, "/api/default-path") => default_path::handle(self, req),
|
||||||
(Method::Post, "/api/exit") => exit::handle(self, req),
|
(Method::Get, "/api/exit") => exit::handle(self, req),
|
||||||
(Method::Get, "/api/packages") => packages::handle(self, req),
|
(Method::Get, "/api/packages") => packages::handle(self, req),
|
||||||
(Method::Get, "/api/installation-status") => installation_status::handle(self, req),
|
(Method::Get, "/api/installation-status") => installation_status::handle(self, req),
|
||||||
|
(Method::Get, "/api/view-local-folder") => view_folder::handle(self, req),
|
||||||
(Method::Post, "/api/check-auth") => authentication::handle(self, req),
|
(Method::Post, "/api/check-auth") => authentication::handle(self, req),
|
||||||
(Method::Post, "/api/start-install") => install::handle(self, req),
|
(Method::Post, "/api/start-install") => install::handle(self, req),
|
||||||
(Method::Post, "/api/open-browser") => browser::handle(self, req),
|
(Method::Post, "/api/open-browser") => browser::handle(self, req),
|
||||||
(Method::Post, "/api/uninstall") => uninstall::handle(self, req),
|
(Method::Post, "/api/uninstall") => uninstall::handle(self, req),
|
||||||
(Method::Post, "/api/update-updater") => update_updater::handle(self, req),
|
(Method::Post, "/api/update-updater") => update_updater::handle(self, req),
|
||||||
|
(Method::Post, "/api/verify-path") => verify_path::handle(self, req),
|
||||||
(Method::Get, _) => static_files::handle(self, req),
|
(Method::Get, _) => static_files::handle(self, req),
|
||||||
e => {
|
e => {
|
||||||
info!("Returned 404 for {:?}", e);
|
info!("Returned 404 for {:?}", e);
|
||||||
|
|
|
@ -2,16 +2,16 @@
|
||||||
//!
|
//!
|
||||||
//! The /api/packages call returns all the currently installed packages.
|
//! The /api/packages call returns all the currently installed packages.
|
||||||
|
|
||||||
use frontend::rest::services::default_future;
|
use crate::frontend::rest::services::default_future;
|
||||||
use frontend::rest::services::encapsulate_json;
|
use crate::frontend::rest::services::encapsulate_json;
|
||||||
use frontend::rest::services::Future;
|
use crate::frontend::rest::services::Future;
|
||||||
use frontend::rest::services::Request;
|
use crate::frontend::rest::services::Request;
|
||||||
use frontend::rest::services::Response;
|
use crate::frontend::rest::services::Response;
|
||||||
use frontend::rest::services::WebService;
|
use crate::frontend::rest::services::WebService;
|
||||||
|
|
||||||
use hyper::header::{ContentLength, ContentType};
|
use hyper::header::{ContentLength, ContentType};
|
||||||
|
|
||||||
use logging::LoggingErrors;
|
use crate::logging::LoggingErrors;
|
||||||
|
|
||||||
pub fn handle(service: &WebService, _req: Request) -> Future {
|
pub fn handle(service: &WebService, _req: Request) -> Future {
|
||||||
let framework = service.get_framework_read();
|
let framework = service.get_framework_read();
|
||||||
|
|
|
@ -4,18 +4,18 @@
|
||||||
//!
|
//!
|
||||||
//! e.g. index.html, main.js, ...
|
//! e.g. index.html, main.js, ...
|
||||||
|
|
||||||
use frontend::rest::assets;
|
use crate::frontend::rest::assets;
|
||||||
|
|
||||||
use frontend::rest::services::default_future;
|
use crate::frontend::rest::services::default_future;
|
||||||
use frontend::rest::services::Future;
|
use crate::frontend::rest::services::Future;
|
||||||
use frontend::rest::services::Request;
|
use crate::frontend::rest::services::Request;
|
||||||
use frontend::rest::services::Response;
|
use crate::frontend::rest::services::Response;
|
||||||
use frontend::rest::services::WebService;
|
use crate::frontend::rest::services::WebService;
|
||||||
|
|
||||||
use hyper::header::{ContentLength, ContentType};
|
use hyper::header::{ContentLength, ContentType};
|
||||||
use hyper::StatusCode;
|
use hyper::StatusCode;
|
||||||
|
|
||||||
use logging::LoggingErrors;
|
use crate::logging::LoggingErrors;
|
||||||
|
|
||||||
pub fn handle(_service: &WebService, req: Request) -> Future {
|
pub fn handle(_service: &WebService, req: Request) -> Future {
|
||||||
// At this point, we have a web browser client. Search for a index page
|
// At this point, we have a web browser client. Search for a index page
|
||||||
|
|
|
@ -2,15 +2,15 @@
|
||||||
//!
|
//!
|
||||||
//! The /api/uninstall call uninstalls all packages.
|
//! The /api/uninstall call uninstalls all packages.
|
||||||
|
|
||||||
use frontend::rest::services::default_future;
|
use crate::frontend::rest::services::default_future;
|
||||||
use frontend::rest::services::stream_progress;
|
use crate::frontend::rest::services::stream_progress;
|
||||||
use frontend::rest::services::Future;
|
use crate::frontend::rest::services::Future;
|
||||||
use frontend::rest::services::Request;
|
use crate::frontend::rest::services::Request;
|
||||||
use frontend::rest::services::WebService;
|
use crate::frontend::rest::services::WebService;
|
||||||
|
|
||||||
use logging::LoggingErrors;
|
use crate::logging::LoggingErrors;
|
||||||
|
|
||||||
use installer::InstallMessage;
|
use crate::installer::InstallMessage;
|
||||||
|
|
||||||
pub fn handle(service: &WebService, _req: Request) -> Future {
|
pub fn handle(service: &WebService, _req: Request) -> Future {
|
||||||
let framework = service.framework.clone();
|
let framework = service.framework.clone();
|
||||||
|
|
|
@ -2,15 +2,15 @@
|
||||||
//!
|
//!
|
||||||
//! The /api/update-updater call attempts to update the currently running updater.
|
//! The /api/update-updater call attempts to update the currently running updater.
|
||||||
|
|
||||||
use frontend::rest::services::default_future;
|
use crate::frontend::rest::services::default_future;
|
||||||
use frontend::rest::services::stream_progress;
|
use crate::frontend::rest::services::stream_progress;
|
||||||
use frontend::rest::services::Future;
|
use crate::frontend::rest::services::Future;
|
||||||
use frontend::rest::services::Request;
|
use crate::frontend::rest::services::Request;
|
||||||
use frontend::rest::services::WebService;
|
use crate::frontend::rest::services::WebService;
|
||||||
|
|
||||||
use logging::LoggingErrors;
|
use crate::logging::LoggingErrors;
|
||||||
|
|
||||||
use installer::InstallMessage;
|
use crate::installer::InstallMessage;
|
||||||
|
|
||||||
pub fn handle(service: &WebService, _req: Request) -> Future {
|
pub fn handle(service: &WebService, _req: Request) -> Future {
|
||||||
let framework = service.framework.clone();
|
let framework = service.framework.clone();
|
||||||
|
|
49
src/frontend/rest/services/verify_path.rs
Normal file
49
src/frontend/rest/services/verify_path.rs
Normal file
|
@ -0,0 +1,49 @@
|
||||||
|
//! frontend/rest/services/verify_path.rs
|
||||||
|
//!
|
||||||
|
//! The /api/verify-path returns whether the path exists or not.
|
||||||
|
|
||||||
|
use crate::frontend::rest::services::Future;
|
||||||
|
use crate::frontend::rest::services::Request;
|
||||||
|
use crate::frontend::rest::services::Response;
|
||||||
|
use crate::frontend::rest::services::WebService;
|
||||||
|
use url::form_urlencoded;
|
||||||
|
|
||||||
|
use hyper::header::{ContentLength, ContentType};
|
||||||
|
|
||||||
|
use futures::future::Future as _;
|
||||||
|
use futures::stream::Stream;
|
||||||
|
|
||||||
|
use crate::logging::LoggingErrors;
|
||||||
|
|
||||||
|
use std::collections::HashMap;
|
||||||
|
use std::path::PathBuf;
|
||||||
|
|
||||||
|
|
||||||
|
/// Struct used by serde to send a JSON payload to the client containing an optional value.
|
||||||
|
#[derive(Serialize)]
|
||||||
|
struct VerifyResponse {
|
||||||
|
exists: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn handle(_service: &WebService, req: Request) -> Future {
|
||||||
|
Box::new(req.body().concat2().map(move |b| {
|
||||||
|
let results = form_urlencoded::parse(b.as_ref())
|
||||||
|
.into_owned()
|
||||||
|
.collect::<HashMap<String, String>>();
|
||||||
|
let mut exists = false;
|
||||||
|
if let Some(path) = results.get("path") {
|
||||||
|
let path = PathBuf::from(path);
|
||||||
|
exists = path.is_dir();
|
||||||
|
}
|
||||||
|
|
||||||
|
let response = VerifyResponse { exists };
|
||||||
|
|
||||||
|
let file = serde_json::to_string(&response)
|
||||||
|
.log_expect("Failed to render JSON payload of default path object");
|
||||||
|
|
||||||
|
Response::new()
|
||||||
|
.with_header(ContentLength(file.len() as u64))
|
||||||
|
.with_header(ContentType::json())
|
||||||
|
.with_body(file)
|
||||||
|
}))
|
||||||
|
}
|
35
src/frontend/rest/services/view_folder.rs
Normal file
35
src/frontend/rest/services/view_folder.rs
Normal file
|
@ -0,0 +1,35 @@
|
||||||
|
//! frontend/rest/services/view_folder.rs
|
||||||
|
//!
|
||||||
|
//! The /api/view-local-folder returns whether the path exists or not.
|
||||||
|
//! Side-effect: will open the folder in the default file manager if it exists.
|
||||||
|
|
||||||
|
use super::default_future;
|
||||||
|
use crate::frontend::rest::services::Future;
|
||||||
|
use crate::frontend::rest::services::Request;
|
||||||
|
use crate::frontend::rest::services::Response;
|
||||||
|
use crate::frontend::rest::services::WebService;
|
||||||
|
|
||||||
|
use hyper::header::{ContentLength, ContentType};
|
||||||
|
|
||||||
|
use crate::logging::LoggingErrors;
|
||||||
|
use crate::native::open_in_shell;
|
||||||
|
|
||||||
|
pub fn handle(service: &WebService, _: Request) -> Future {
|
||||||
|
let framework = service.get_framework_read();
|
||||||
|
let mut response = false;
|
||||||
|
let path = framework.install_path.clone();
|
||||||
|
if let Some(path) = path {
|
||||||
|
response = true;
|
||||||
|
open_in_shell(path.as_path());
|
||||||
|
}
|
||||||
|
|
||||||
|
let file = serde_json::to_string(&response)
|
||||||
|
.log_expect("Failed to render JSON payload of installation status object");
|
||||||
|
|
||||||
|
default_future(
|
||||||
|
Response::new()
|
||||||
|
.with_header(ContentLength(file.len() as u64))
|
||||||
|
.with_header(ContentType::json())
|
||||||
|
.with_body(file),
|
||||||
|
)
|
||||||
|
}
|
|
@ -4,7 +4,7 @@
|
||||||
|
|
||||||
use web_view::Content;
|
use web_view::Content;
|
||||||
|
|
||||||
use logging::LoggingErrors;
|
use crate::logging::LoggingErrors;
|
||||||
|
|
||||||
use log::Level;
|
use log::Level;
|
||||||
|
|
||||||
|
@ -16,8 +16,8 @@ enum CallbackType {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Starts the main web UI. Will return when UI is closed.
|
/// Starts the main web UI. Will return when UI is closed.
|
||||||
pub fn start_ui(app_name: &str, http_address: &str, _is_launcher: bool) {
|
pub fn start_ui(app_name: &str, http_address: &str, is_launcher: bool) {
|
||||||
let size = (1024, 550);
|
let size = if is_launcher { (600, 300) } else { (1024, 500) };
|
||||||
|
|
||||||
info!("Spawning web view instance");
|
info!("Spawning web view instance");
|
||||||
|
|
||||||
|
@ -26,7 +26,7 @@ pub fn start_ui(app_name: &str, http_address: &str, _is_launcher: bool) {
|
||||||
.content(Content::Url(http_address))
|
.content(Content::Url(http_address))
|
||||||
.size(size.0, size.1)
|
.size(size.0, size.1)
|
||||||
.resizable(false)
|
.resizable(false)
|
||||||
.debug(false)
|
.debug(cfg!(debug_assertions))
|
||||||
.user_data(())
|
.user_data(())
|
||||||
.invoke_handler(|wv, msg| {
|
.invoke_handler(|wv, msg| {
|
||||||
let mut cb_result = Ok(());
|
let mut cb_result = Ok(());
|
||||||
|
@ -37,15 +37,14 @@ pub fn start_ui(app_name: &str, http_address: &str, _is_launcher: bool) {
|
||||||
|
|
||||||
match command {
|
match command {
|
||||||
CallbackType::SelectInstallDir { callback_name } => {
|
CallbackType::SelectInstallDir { callback_name } => {
|
||||||
let result = wv
|
let result =
|
||||||
.dialog()
|
tinyfiledialogs::select_folder_dialog("Select a install directory...", "");
|
||||||
.choose_directory("Select a install directory...", "");
|
|
||||||
|
|
||||||
if let Ok(Some(new_path)) = result {
|
if let Some(new_path) = result {
|
||||||
if new_path.to_string_lossy().len() > 0 {
|
if !new_path.is_empty() {
|
||||||
let result = serde_json::to_string(&new_path)
|
let result = serde_json::to_string(&new_path)
|
||||||
.log_expect("Unable to serialize response");
|
.log_expect("Unable to serialize response");
|
||||||
let command = format!("{}({});", callback_name, result);
|
let command = format!("window.{}({});", callback_name, result);
|
||||||
debug!("Injecting response: {}", command);
|
debug!("Injecting response: {}", command);
|
||||||
cb_result = wv.eval(&command);
|
cb_result = wv.eval(&command);
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,7 +7,7 @@ use reqwest::header::CONTENT_LENGTH;
|
||||||
use std::io::Read;
|
use std::io::Read;
|
||||||
use std::time::Duration;
|
use std::time::Duration;
|
||||||
|
|
||||||
use reqwest::async::Client as AsyncClient;
|
use reqwest::r#async::Client as AsyncClient;
|
||||||
use reqwest::Client;
|
use reqwest::Client;
|
||||||
use reqwest::StatusCode;
|
use reqwest::StatusCode;
|
||||||
|
|
||||||
|
|
|
@ -21,28 +21,28 @@ use std::io::Cursor;
|
||||||
use std::process::Command;
|
use std::process::Command;
|
||||||
use std::process::{exit, Stdio};
|
use std::process::{exit, Stdio};
|
||||||
|
|
||||||
use config::BaseAttributes;
|
use crate::config::BaseAttributes;
|
||||||
use config::Config;
|
use crate::config::Config;
|
||||||
|
|
||||||
use sources::types::Version;
|
use crate::sources::types::Version;
|
||||||
|
|
||||||
use tasks::install::InstallTask;
|
use crate::tasks::install::InstallTask;
|
||||||
use tasks::uninstall::UninstallTask;
|
use crate::tasks::uninstall::UninstallTask;
|
||||||
use tasks::uninstall_global_shortcut::UninstallGlobalShortcutsTask;
|
use crate::tasks::uninstall_global_shortcut::UninstallGlobalShortcutsTask;
|
||||||
use tasks::DependencyTree;
|
use crate::tasks::DependencyTree;
|
||||||
use tasks::TaskMessage;
|
use crate::tasks::TaskMessage;
|
||||||
|
|
||||||
use logging::LoggingErrors;
|
use crate::logging::LoggingErrors;
|
||||||
|
|
||||||
use dirs::home_dir;
|
use dirs::home_dir;
|
||||||
|
|
||||||
use std::fs::remove_file;
|
use std::fs::remove_file;
|
||||||
|
|
||||||
use http;
|
use crate::http;
|
||||||
|
|
||||||
use number_prefix::{NumberPrefix, Prefixed, Standalone};
|
use number_prefix::NumberPrefix::{self, Prefixed, Standalone};
|
||||||
|
|
||||||
use native;
|
use crate::native;
|
||||||
|
|
||||||
/// A message thrown during the installation of packages.
|
/// A message thrown during the installation of packages.
|
||||||
#[derive(Serialize)]
|
#[derive(Serialize)]
|
||||||
|
@ -174,12 +174,14 @@ impl InstallerFramework {
|
||||||
/// items: Array of named packages to be installed/kept
|
/// items: Array of named packages to be installed/kept
|
||||||
/// messages: Channel used to send progress messages
|
/// messages: Channel used to send progress messages
|
||||||
/// fresh_install: If the install directory must be empty
|
/// fresh_install: If the install directory must be empty
|
||||||
|
/// force_install: If the install directory should be erased first
|
||||||
pub fn install(
|
pub fn install(
|
||||||
&mut self,
|
&mut self,
|
||||||
items: Vec<String>,
|
items: Vec<String>,
|
||||||
messages: &Sender<InstallMessage>,
|
messages: &Sender<InstallMessage>,
|
||||||
fresh_install: bool,
|
fresh_install: bool,
|
||||||
create_desktop_shortcuts: bool,
|
create_desktop_shortcuts: bool,
|
||||||
|
force_install: bool,
|
||||||
) -> Result<(), String> {
|
) -> Result<(), String> {
|
||||||
info!(
|
info!(
|
||||||
"Framework: Installing {:?} to {:?}",
|
"Framework: Installing {:?} to {:?}",
|
||||||
|
@ -209,6 +211,7 @@ impl InstallerFramework {
|
||||||
uninstall_items,
|
uninstall_items,
|
||||||
fresh_install,
|
fresh_install,
|
||||||
create_desktop_shortcuts,
|
create_desktop_shortcuts,
|
||||||
|
force_install
|
||||||
});
|
});
|
||||||
|
|
||||||
let mut tree = DependencyTree::build(task);
|
let mut tree = DependencyTree::build(task);
|
||||||
|
|
|
@ -74,7 +74,7 @@ use config::BaseAttributes;
|
||||||
use std::process::{Command, Stdio, exit};
|
use std::process::{Command, Stdio, exit};
|
||||||
use std::fs;
|
use std::fs;
|
||||||
|
|
||||||
static RAW_CONFIG: &'static str = include_str!(concat!(env!("OUT_DIR"), "/bootstrap.toml"));
|
const RAW_CONFIG: &str = include_str!(concat!(env!("OUT_DIR"), "/bootstrap.toml"));
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
let config = BaseAttributes::from_toml_str(RAW_CONFIG).expect("Config file could not be read");
|
let config = BaseAttributes::from_toml_str(RAW_CONFIG).expect("Config file could not be read");
|
||||||
|
@ -109,6 +109,7 @@ fn main() {
|
||||||
|
|
||||||
info!("{} installer", app_name);
|
info!("{} installer", app_name);
|
||||||
|
|
||||||
|
// Handle self-updating if needed
|
||||||
let current_exe = std::env::current_exe().log_expect("Current executable could not be found");
|
let current_exe = std::env::current_exe().log_expect("Current executable could not be found");
|
||||||
let current_path = current_exe
|
let current_path = current_exe
|
||||||
.parent()
|
.parent()
|
||||||
|
|
|
@ -46,7 +46,7 @@ extern "C" int saveShortcut(
|
||||||
const wchar_t *workingDir,
|
const wchar_t *workingDir,
|
||||||
const wchar_t *exePath)
|
const wchar_t *exePath)
|
||||||
{
|
{
|
||||||
const char *errStr = NULL;
|
char *errStr = NULL;
|
||||||
HRESULT h;
|
HRESULT h;
|
||||||
IShellLink *shellLink = NULL;
|
IShellLink *shellLink = NULL;
|
||||||
IPersistFile *persistFile = NULL;
|
IPersistFile *persistFile = NULL;
|
||||||
|
@ -168,15 +168,3 @@ extern "C" HRESULT getSystemFolder(wchar_t *out_path)
|
||||||
}
|
}
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
extern "C" HRESULT getDesktopFolder(wchar_t *out_path)
|
|
||||||
{
|
|
||||||
PWSTR path = NULL;
|
|
||||||
HRESULT result = SHGetKnownFolderPath(FOLDERID_Desktop, 0, NULL, &path);
|
|
||||||
if (result == S_OK)
|
|
||||||
{
|
|
||||||
wcscpy_s(out_path, MAX_PATH + 1, path);
|
|
||||||
CoTaskMemFree(path);
|
|
||||||
}
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
|
@ -15,9 +15,12 @@ mod natives {
|
||||||
|
|
||||||
const PROCESS_LEN: usize = 10192;
|
const PROCESS_LEN: usize = 10192;
|
||||||
|
|
||||||
use logging::LoggingErrors;
|
use crate::logging::LoggingErrors;
|
||||||
|
|
||||||
use std::env;
|
use std::env;
|
||||||
|
use std::os::windows::ffi::OsStrExt;
|
||||||
|
use std::path::Path;
|
||||||
|
use std::process::Command;
|
||||||
|
|
||||||
use winapi::shared::minwindef::{DWORD, FALSE, MAX_PATH};
|
use winapi::shared::minwindef::{DWORD, FALSE, MAX_PATH};
|
||||||
|
|
||||||
|
@ -26,11 +29,13 @@ mod natives {
|
||||||
use winapi::um::psapi::{
|
use winapi::um::psapi::{
|
||||||
EnumProcessModulesEx, GetModuleFileNameExW, K32EnumProcesses, LIST_MODULES_ALL,
|
EnumProcessModulesEx, GetModuleFileNameExW, K32EnumProcesses, LIST_MODULES_ALL,
|
||||||
};
|
};
|
||||||
|
use winapi::um::shellapi::ShellExecuteW;
|
||||||
use winapi::um::winnt::{
|
use winapi::um::winnt::{
|
||||||
HANDLE, PROCESS_QUERY_INFORMATION, PROCESS_TERMINATE, PROCESS_VM_READ,
|
HANDLE, PROCESS_QUERY_INFORMATION, PROCESS_TERMINATE, PROCESS_VM_READ,
|
||||||
};
|
};
|
||||||
|
use winapi::um::winuser::SW_SHOWDEFAULT;
|
||||||
|
|
||||||
use widestring::{U16CString};
|
use widestring::U16CString;
|
||||||
|
|
||||||
extern "C" {
|
extern "C" {
|
||||||
pub fn saveShortcut(
|
pub fn saveShortcut(
|
||||||
|
@ -50,28 +55,6 @@ mod natives {
|
||||||
) -> ::std::os::raw::c_int;
|
) -> ::std::os::raw::c_int;
|
||||||
|
|
||||||
pub fn getSystemFolder(out_path: *mut ::std::os::raw::c_ushort) -> HRESULT;
|
pub fn getSystemFolder(out_path: *mut ::std::os::raw::c_ushort) -> HRESULT;
|
||||||
|
|
||||||
pub fn getDesktopFolder(out_path: *mut ::std::os::raw::c_ushort) -> HRESULT;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Needed here for Windows interop
|
|
||||||
#[allow(unsafe_code)]
|
|
||||||
pub fn create_desktop_shortcut(
|
|
||||||
name: &str,
|
|
||||||
description: &str,
|
|
||||||
target: &str,
|
|
||||||
args: &str,
|
|
||||||
working_dir: &str,
|
|
||||||
exe_path: &str,
|
|
||||||
) -> Result<String, String> {
|
|
||||||
let mut cmd_path = [0u16; MAX_PATH + 1];
|
|
||||||
let _result = unsafe { getDesktopFolder(cmd_path.as_mut_ptr()) };
|
|
||||||
let source_path = format!(
|
|
||||||
"{}\\{}.lnk",
|
|
||||||
String::from_utf16_lossy(&cmd_path[..count_u16(&cmd_path)]).as_str(),
|
|
||||||
name
|
|
||||||
);
|
|
||||||
create_shortcut_inner(source_path, name, description, target, args, working_dir, exe_path)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Needed here for Windows interop
|
// Needed here for Windows interop
|
||||||
|
@ -139,15 +122,23 @@ mod natives {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn count_u16(u16str: &[u16]) -> usize {
|
// Needed to call unsafe function `ShellExecuteW` from `winapi` crate
|
||||||
let mut pos = 0;
|
#[allow(unsafe_code)]
|
||||||
for x in u16str.iter() {
|
pub fn open_in_shell(path: &Path) {
|
||||||
if *x == 0 {
|
let native_verb = U16CString::from_str("open").unwrap();
|
||||||
break;
|
// https://doc.rust-lang.org/std/os/windows/ffi/trait.OsStrExt.html#tymethod.encode_wide
|
||||||
}
|
let mut native_path: Vec<u16> = path.as_os_str().encode_wide().collect();
|
||||||
pos += 1;
|
native_path.push(0); // NULL terminator
|
||||||
|
unsafe {
|
||||||
|
ShellExecuteW(
|
||||||
|
std::ptr::null_mut(),
|
||||||
|
native_verb.as_ptr(),
|
||||||
|
native_path.as_ptr(),
|
||||||
|
std::ptr::null_mut(),
|
||||||
|
std::ptr::null_mut(),
|
||||||
|
SW_SHOWDEFAULT,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
pos
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Cleans up the installer
|
/// Cleans up the installer
|
||||||
|
@ -179,13 +170,20 @@ mod natives {
|
||||||
let spawn_result: i32 = unsafe {
|
let spawn_result: i32 = unsafe {
|
||||||
let mut cmd_path = [0u16; MAX_PATH + 1];
|
let mut cmd_path = [0u16; MAX_PATH + 1];
|
||||||
let result = getSystemFolder(cmd_path.as_mut_ptr());
|
let result = getSystemFolder(cmd_path.as_mut_ptr());
|
||||||
|
let mut pos = 0;
|
||||||
|
for x in cmd_path.iter() {
|
||||||
|
if *x == 0 {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
pos += 1;
|
||||||
|
}
|
||||||
if result != winapi::shared::winerror::S_OK {
|
if result != winapi::shared::winerror::S_OK {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
spawnDetached(
|
spawnDetached(
|
||||||
U16CString::from_str(
|
U16CString::from_str(
|
||||||
format!("{}\\cmd.exe", String::from_utf16_lossy(&cmd_path[..count_u16(&cmd_path)])).as_str(),
|
format!("{}\\cmd.exe", String::from_utf16_lossy(&cmd_path[..pos])).as_str(),
|
||||||
)
|
)
|
||||||
.log_expect("Unable to convert string to wchar_t")
|
.log_expect("Unable to convert string to wchar_t")
|
||||||
.as_ptr(),
|
.as_ptr(),
|
||||||
|
@ -297,7 +295,7 @@ mod natives {
|
||||||
|
|
||||||
use std::env;
|
use std::env;
|
||||||
|
|
||||||
use logging::LoggingErrors;
|
use crate::logging::LoggingErrors;
|
||||||
|
|
||||||
use sysinfo::{ProcessExt, SystemExt};
|
use sysinfo::{ProcessExt, SystemExt};
|
||||||
|
|
||||||
|
@ -306,6 +304,8 @@ mod natives {
|
||||||
use slug::slugify;
|
use slug::slugify;
|
||||||
use std::fs::{create_dir_all, File};
|
use std::fs::{create_dir_all, File};
|
||||||
use std::io::Write;
|
use std::io::Write;
|
||||||
|
use std::path::Path;
|
||||||
|
use std::process::Command;
|
||||||
|
|
||||||
#[cfg(target_os = "linux")]
|
#[cfg(target_os = "linux")]
|
||||||
pub fn create_shortcut(
|
pub fn create_shortcut(
|
||||||
|
@ -314,6 +314,7 @@ mod natives {
|
||||||
target: &str,
|
target: &str,
|
||||||
args: &str,
|
args: &str,
|
||||||
working_dir: &str,
|
working_dir: &str,
|
||||||
|
_exe_path: &str,
|
||||||
) -> Result<String, String> {
|
) -> Result<String, String> {
|
||||||
// FIXME: no icon will be shown since no icon is provided
|
// FIXME: no icon will be shown since no icon is provided
|
||||||
let data_local_dir = dirs::data_local_dir();
|
let data_local_dir = dirs::data_local_dir();
|
||||||
|
@ -322,7 +323,7 @@ mod natives {
|
||||||
let mut path = x;
|
let mut path = x;
|
||||||
path.push("applications");
|
path.push("applications");
|
||||||
match create_dir_all(path.to_path_buf()) {
|
match create_dir_all(path.to_path_buf()) {
|
||||||
Ok(_) => (()),
|
Ok(_) => (),
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
return Err(format!(
|
return Err(format!(
|
||||||
"Local data directory does not exist and cannot be created: {}",
|
"Local data directory does not exist and cannot be created: {}",
|
||||||
|
@ -340,7 +341,7 @@ mod natives {
|
||||||
Ok(file) => file,
|
Ok(file) => file,
|
||||||
Err(e) => return Err(format!("Unable to create desktop file: {}", e)),
|
Err(e) => return Err(format!("Unable to create desktop file: {}", e)),
|
||||||
};
|
};
|
||||||
let mut desktop_f = desktop_f.write_all(desktop_file.as_bytes());
|
let desktop_f = desktop_f.write_all(desktop_file.as_bytes());
|
||||||
match desktop_f {
|
match desktop_f {
|
||||||
Ok(_) => Ok("".to_string()),
|
Ok(_) => Ok("".to_string()),
|
||||||
Err(e) => Err(format!("Unable to write desktop file: {}", e)),
|
Err(e) => Err(format!("Unable to write desktop file: {}", e)),
|
||||||
|
@ -358,11 +359,25 @@ mod natives {
|
||||||
target: &str,
|
target: &str,
|
||||||
args: &str,
|
args: &str,
|
||||||
working_dir: &str,
|
working_dir: &str,
|
||||||
|
_exe_path: &str,
|
||||||
) -> Result<String, String> {
|
) -> Result<String, String> {
|
||||||
warn!("STUB! Creating shortcut is not implemented on macOS");
|
warn!("STUB! Creating shortcut is not implemented on macOS");
|
||||||
Ok("".to_string())
|
Ok("".to_string())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn open_in_shell(path: &Path) {
|
||||||
|
let shell: &str;
|
||||||
|
if cfg!(target_os = "linux") {
|
||||||
|
shell = "xdg-open";
|
||||||
|
} else if cfg!(target_os = "macos") {
|
||||||
|
shell = "open";
|
||||||
|
} else {
|
||||||
|
warn!("Unsupported platform");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
Command::new(shell).arg(path).spawn().ok();
|
||||||
|
}
|
||||||
|
|
||||||
/// Cleans up the installer
|
/// Cleans up the installer
|
||||||
pub fn burn_on_exit(app_name: &str) {
|
pub fn burn_on_exit(app_name: &str) {
|
||||||
let current_exe = env::current_exe().log_expect("Current executable could not be found");
|
let current_exe = env::current_exe().log_expect("Current executable could not be found");
|
||||||
|
@ -387,7 +402,7 @@ mod natives {
|
||||||
let mut processes: Vec<super::Process> = Vec::new();
|
let mut processes: Vec<super::Process> = Vec::new();
|
||||||
let mut system = sysinfo::System::new();
|
let mut system = sysinfo::System::new();
|
||||||
system.refresh_all();
|
system.refresh_all();
|
||||||
for (pid, procs) in system.get_process_list() {
|
for (pid, procs) in system.get_processes() {
|
||||||
processes.push(super::Process {
|
processes.push(super::Process {
|
||||||
pid: *pid as usize,
|
pid: *pid as usize,
|
||||||
name: procs.name().to_string(),
|
name: procs.name().to_string(),
|
||||||
|
|
|
@ -9,7 +9,7 @@ use std::{thread, time};
|
||||||
|
|
||||||
use clap::{App, ArgMatches};
|
use clap::{App, ArgMatches};
|
||||||
|
|
||||||
use logging::LoggingErrors;
|
use crate::logging::LoggingErrors;
|
||||||
|
|
||||||
/// Swaps around the main executable if needed.
|
/// Swaps around the main executable if needed.
|
||||||
pub fn perform_swap(current_exe: &PathBuf, to_path: Option<&str>) {
|
pub fn perform_swap(current_exe: &PathBuf, to_path: Option<&str>) {
|
||||||
|
|
|
@ -7,9 +7,9 @@ use reqwest::StatusCode;
|
||||||
|
|
||||||
use serde_json;
|
use serde_json;
|
||||||
|
|
||||||
use sources::types::*;
|
use crate::sources::types::*;
|
||||||
|
|
||||||
use http::build_client;
|
use crate::http::build_client;
|
||||||
|
|
||||||
pub struct GithubReleases {}
|
pub struct GithubReleases {}
|
||||||
|
|
||||||
|
|
|
@ -24,7 +24,7 @@ impl Version {
|
||||||
match *self {
|
match *self {
|
||||||
Version::Semver(ref version) => version.to_owned(),
|
Version::Semver(ref version) => version.to_owned(),
|
||||||
Version::Integer(ref version) => {
|
Version::Integer(ref version) => {
|
||||||
SemverVersion::from((version.to_owned(), 0 as u64, 0 as u64))
|
SemverVersion::new(version.to_owned(), 0u64, 0u64)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,15 +1,21 @@
|
||||||
//! Downloads a package into memory.
|
//! Downloads a package into memory.
|
||||||
|
|
||||||
use installer::InstallerFramework;
|
use crate::installer::InstallerFramework;
|
||||||
|
|
||||||
use tasks::check_authorization::CheckAuthorizationTask;
|
use crate::tasks::check_authorization::CheckAuthorizationTask;
|
||||||
use tasks::{Task, TaskDependency, TaskMessage, TaskOrdering, TaskParamType};
|
use crate::tasks::Task;
|
||||||
|
use crate::tasks::TaskDependency;
|
||||||
|
use crate::tasks::TaskMessage;
|
||||||
|
use crate::tasks::TaskOrdering;
|
||||||
|
use crate::tasks::TaskParamType;
|
||||||
|
|
||||||
use http::stream_file;
|
use crate::tasks::resolver::ResolvePackageTask;
|
||||||
|
|
||||||
use number_prefix::{NumberPrefix, Prefixed, Standalone};
|
use crate::http::stream_file;
|
||||||
|
|
||||||
use logging::LoggingErrors;
|
use number_prefix::NumberPrefix::{self, Prefixed, Standalone};
|
||||||
|
|
||||||
|
use crate::logging::LoggingErrors;
|
||||||
|
|
||||||
pub struct DownloadPackageTask {
|
pub struct DownloadPackageTask {
|
||||||
pub name: String,
|
pub name: String,
|
||||||
|
|
|
@ -1,14 +1,14 @@
|
||||||
//! Verifies that this is the only running instance of the installer, and that no application is running.
|
//! Verifies that this is the only running instance of the installer, and that no application is running.
|
||||||
|
|
||||||
use installer::InstallerFramework;
|
use crate::installer::InstallerFramework;
|
||||||
|
|
||||||
use tasks::Task;
|
use crate::tasks::Task;
|
||||||
use tasks::TaskDependency;
|
use crate::tasks::TaskDependency;
|
||||||
use tasks::TaskMessage;
|
use crate::tasks::TaskMessage;
|
||||||
use tasks::TaskParamType;
|
use crate::tasks::TaskParamType;
|
||||||
|
|
||||||
use native::get_process_names;
|
use crate::native::get_process_names;
|
||||||
use native::Process;
|
use crate::native::Process;
|
||||||
|
|
||||||
use std::process;
|
use std::process;
|
||||||
|
|
||||||
|
|
|
@ -1,26 +1,29 @@
|
||||||
//! Overall hierarchy for installing a installation of the application.
|
//! Overall hierarchy for installing a installation of the application.
|
||||||
|
|
||||||
use installer::InstallerFramework;
|
use crate::installer::InstallerFramework;
|
||||||
|
|
||||||
use tasks::ensure_only_instance::EnsureOnlyInstanceTask;
|
use crate::tasks::ensure_only_instance::EnsureOnlyInstanceTask;
|
||||||
use tasks::install_dir::VerifyInstallDirTask;
|
use crate::tasks::install_dir::VerifyInstallDirTask;
|
||||||
use tasks::install_global_shortcut::InstallGlobalShortcutsTask;
|
use crate::tasks::install_global_shortcut::InstallGlobalShortcutsTask;
|
||||||
use tasks::install_pkg::InstallPackageTask;
|
use crate::tasks::install_pkg::InstallPackageTask;
|
||||||
use tasks::save_executable::SaveExecutableTask;
|
use crate::tasks::remove_target_dir::RemoveTargetDirTask;
|
||||||
use tasks::uninstall_pkg::UninstallPackageTask;
|
use crate::tasks::save_executable::SaveExecutableTask;
|
||||||
use tasks::launch_installed_on_exit::LaunchOnExitTask;
|
use crate::tasks::uninstall_pkg::UninstallPackageTask;
|
||||||
|
use crate::tasks::launch_installed_on_exit::LaunchOnExitTask;
|
||||||
|
|
||||||
use tasks::Task;
|
use crate::tasks::Task;
|
||||||
use tasks::TaskDependency;
|
use crate::tasks::TaskDependency;
|
||||||
use tasks::TaskMessage;
|
use crate::tasks::TaskMessage;
|
||||||
use tasks::TaskOrdering;
|
use crate::tasks::TaskOrdering;
|
||||||
use tasks::TaskParamType;
|
use crate::tasks::TaskParamType;
|
||||||
|
|
||||||
pub struct InstallTask {
|
pub struct InstallTask {
|
||||||
pub items: Vec<String>,
|
pub items: Vec<String>,
|
||||||
pub uninstall_items: Vec<String>,
|
pub uninstall_items: Vec<String>,
|
||||||
pub fresh_install: bool,
|
pub fresh_install: bool,
|
||||||
pub create_desktop_shortcuts: bool,
|
pub create_desktop_shortcuts: bool,
|
||||||
|
// force_install: remove the target directory before installing
|
||||||
|
pub force_install: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Task for InstallTask {
|
impl Task for InstallTask {
|
||||||
|
@ -42,6 +45,13 @@ impl Task for InstallTask {
|
||||||
Box::new(EnsureOnlyInstanceTask {}),
|
Box::new(EnsureOnlyInstanceTask {}),
|
||||||
));
|
));
|
||||||
|
|
||||||
|
if self.force_install {
|
||||||
|
elements.push(TaskDependency::build(
|
||||||
|
TaskOrdering::Pre,
|
||||||
|
Box::new(RemoveTargetDirTask {}),
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
elements.push(TaskDependency::build(
|
elements.push(TaskDependency::build(
|
||||||
TaskOrdering::Pre,
|
TaskOrdering::Pre,
|
||||||
Box::new(VerifyInstallDirTask {
|
Box::new(VerifyInstallDirTask {
|
||||||
|
|
|
@ -1,16 +1,16 @@
|
||||||
//! Verifies properties about the installation directory.
|
//! Verifies properties about the installation directory.
|
||||||
|
|
||||||
use installer::InstallerFramework;
|
use crate::installer::InstallerFramework;
|
||||||
|
|
||||||
use tasks::Task;
|
use crate::tasks::Task;
|
||||||
use tasks::TaskDependency;
|
use crate::tasks::TaskDependency;
|
||||||
use tasks::TaskMessage;
|
use crate::tasks::TaskMessage;
|
||||||
use tasks::TaskParamType;
|
use crate::tasks::TaskParamType;
|
||||||
|
|
||||||
use std::fs::create_dir_all;
|
use std::fs::create_dir_all;
|
||||||
use std::fs::read_dir;
|
use std::fs::read_dir;
|
||||||
|
|
||||||
use logging::LoggingErrors;
|
use crate::logging::LoggingErrors;
|
||||||
|
|
||||||
pub struct VerifyInstallDirTask {
|
pub struct VerifyInstallDirTask {
|
||||||
pub clean_install: bool,
|
pub clean_install: bool,
|
||||||
|
|
|
@ -1,17 +1,17 @@
|
||||||
//! Generates the global shortcut for this application.
|
//! Generates the global shortcut for this application.
|
||||||
|
|
||||||
use installer::InstallerFramework;
|
use crate::installer::InstallerFramework;
|
||||||
|
|
||||||
use tasks::Task;
|
use crate::tasks::Task;
|
||||||
use tasks::TaskDependency;
|
use crate::tasks::TaskDependency;
|
||||||
use tasks::TaskMessage;
|
use crate::tasks::TaskMessage;
|
||||||
use tasks::TaskParamType;
|
use crate::tasks::TaskParamType;
|
||||||
|
|
||||||
use logging::LoggingErrors;
|
use crate::logging::LoggingErrors;
|
||||||
|
|
||||||
use native::create_shortcut;
|
use crate::native::create_shortcut;
|
||||||
use tasks::save_database::SaveDatabaseTask;
|
use crate::tasks::save_database::SaveDatabaseTask;
|
||||||
use tasks::TaskOrdering;
|
use crate::tasks::TaskOrdering;
|
||||||
|
|
||||||
pub struct InstallGlobalShortcutsTask {}
|
pub struct InstallGlobalShortcutsTask {}
|
||||||
|
|
||||||
|
|
|
@ -1,26 +1,26 @@
|
||||||
//! Installs a specific package.
|
//! Installs a specific package.
|
||||||
|
|
||||||
use installer::InstallerFramework;
|
use crate::installer::InstallerFramework;
|
||||||
|
|
||||||
use tasks::download_pkg::DownloadPackageTask;
|
use crate::tasks::download_pkg::DownloadPackageTask;
|
||||||
use tasks::install_shortcuts::InstallShortcutsTask;
|
use crate::tasks::install_shortcuts::InstallShortcutsTask;
|
||||||
use tasks::save_database::SaveDatabaseTask;
|
use crate::tasks::save_database::SaveDatabaseTask;
|
||||||
use tasks::uninstall_pkg::UninstallPackageTask;
|
use crate::tasks::uninstall_pkg::UninstallPackageTask;
|
||||||
use tasks::Task;
|
use crate::tasks::Task;
|
||||||
use tasks::TaskDependency;
|
use crate::tasks::TaskDependency;
|
||||||
use tasks::TaskMessage;
|
use crate::tasks::TaskMessage;
|
||||||
use tasks::TaskOrdering;
|
use crate::tasks::TaskOrdering;
|
||||||
use tasks::TaskParamType;
|
use crate::tasks::TaskParamType;
|
||||||
|
|
||||||
use config::PackageDescription;
|
use crate::config::PackageDescription;
|
||||||
use installer::LocalInstallation;
|
use crate::installer::LocalInstallation;
|
||||||
|
|
||||||
use std::fs::create_dir_all;
|
use std::fs::create_dir_all;
|
||||||
use std::io::copy;
|
use std::io::copy;
|
||||||
|
|
||||||
use logging::LoggingErrors;
|
use crate::logging::LoggingErrors;
|
||||||
|
|
||||||
use archives;
|
use crate::archives;
|
||||||
|
|
||||||
use std::fs::OpenOptions;
|
use std::fs::OpenOptions;
|
||||||
use std::path::Path;
|
use std::path::Path;
|
||||||
|
@ -136,7 +136,7 @@ impl Task for InstallPackageTask {
|
||||||
info!("Creating file: {:?}", string_name);
|
info!("Creating file: {:?}", string_name);
|
||||||
|
|
||||||
if !installed_files.contains(&string_name) {
|
if !installed_files.contains(&string_name) {
|
||||||
installed_files.push(string_name.to_string());
|
installed_files.push(string_name);
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut file_metadata = OpenOptions::new();
|
let mut file_metadata = OpenOptions::new();
|
||||||
|
@ -165,7 +165,7 @@ impl Task for InstallPackageTask {
|
||||||
|
|
||||||
// Save metadata about this package
|
// Save metadata about this package
|
||||||
context.database.packages.push(LocalInstallation {
|
context.database.packages.push(LocalInstallation {
|
||||||
name: package.name.to_owned(),
|
name: package.name,
|
||||||
version,
|
version,
|
||||||
shortcuts: Vec::new(),
|
shortcuts: Vec::new(),
|
||||||
files: installed_files,
|
files: installed_files,
|
||||||
|
|
|
@ -1,17 +1,17 @@
|
||||||
//! Generates shortcuts for a specified file.
|
//! Generates shortcuts for a specified file.
|
||||||
|
|
||||||
use installer::InstallerFramework;
|
use crate::installer::InstallerFramework;
|
||||||
|
|
||||||
use tasks::Task;
|
use crate::tasks::Task;
|
||||||
use tasks::TaskDependency;
|
use crate::tasks::TaskDependency;
|
||||||
use tasks::TaskMessage;
|
use crate::tasks::TaskMessage;
|
||||||
use tasks::TaskParamType;
|
use crate::tasks::TaskParamType;
|
||||||
|
|
||||||
use config::PackageDescription;
|
use crate::config::PackageDescription;
|
||||||
|
|
||||||
use logging::LoggingErrors;
|
use crate::logging::LoggingErrors;
|
||||||
|
|
||||||
use native::create_shortcut;
|
use crate::native::create_shortcut;
|
||||||
|
|
||||||
pub struct InstallShortcutsTask {
|
pub struct InstallShortcutsTask {
|
||||||
pub name: String,
|
pub name: String,
|
||||||
|
|
|
@ -4,10 +4,10 @@
|
||||||
use std::fmt;
|
use std::fmt;
|
||||||
use std::fmt::Display;
|
use std::fmt::Display;
|
||||||
|
|
||||||
use installer::InstallerFramework;
|
use crate::installer::InstallerFramework;
|
||||||
|
|
||||||
use sources::types::File;
|
use crate::sources::types::File;
|
||||||
use sources::types::Version;
|
use crate::sources::types::Version;
|
||||||
|
|
||||||
pub mod check_authorization;
|
pub mod check_authorization;
|
||||||
pub mod download_pkg;
|
pub mod download_pkg;
|
||||||
|
@ -26,6 +26,7 @@ pub mod uninstall;
|
||||||
pub mod uninstall_global_shortcut;
|
pub mod uninstall_global_shortcut;
|
||||||
pub mod uninstall_pkg;
|
pub mod uninstall_pkg;
|
||||||
pub mod uninstall_shortcuts;
|
pub mod uninstall_shortcuts;
|
||||||
|
pub mod remove_target_dir;
|
||||||
|
|
||||||
/// An abstraction over the various parameters that can be passed around.
|
/// An abstraction over the various parameters that can be passed around.
|
||||||
pub enum TaskParamType {
|
pub enum TaskParamType {
|
||||||
|
|
64
src/tasks/remove_target_dir.rs
Normal file
64
src/tasks/remove_target_dir.rs
Normal file
|
@ -0,0 +1,64 @@
|
||||||
|
//! remove the whole target directory from the existence
|
||||||
|
|
||||||
|
use crate::installer::InstallerFramework;
|
||||||
|
|
||||||
|
use crate::tasks::Task;
|
||||||
|
use crate::tasks::TaskDependency;
|
||||||
|
use crate::tasks::TaskMessage;
|
||||||
|
use crate::tasks::TaskParamType;
|
||||||
|
|
||||||
|
pub struct RemoveTargetDirTask {}
|
||||||
|
|
||||||
|
impl Task for RemoveTargetDirTask {
|
||||||
|
fn execute(
|
||||||
|
&mut self,
|
||||||
|
_: Vec<TaskParamType>,
|
||||||
|
context: &mut InstallerFramework,
|
||||||
|
messenger: &dyn Fn(&TaskMessage),
|
||||||
|
) -> Result<TaskParamType, String> {
|
||||||
|
messenger(&TaskMessage::DisplayMessage(
|
||||||
|
"Removing previous install...",
|
||||||
|
0.1,
|
||||||
|
));
|
||||||
|
// erase the database as well
|
||||||
|
context.database.packages = Vec::new();
|
||||||
|
if let Some(path) = context.install_path.as_ref() {
|
||||||
|
let entries = std::fs::read_dir(path)
|
||||||
|
.map_err(|e| format!("Error reading {}: {}", path.to_string_lossy(), e))?;
|
||||||
|
// remove everything under the path
|
||||||
|
if !context.preexisting_install {
|
||||||
|
std::fs::remove_dir_all(path)
|
||||||
|
.map_err(|e| format!("Error removing {}: {}", path.to_string_lossy(), e))?;
|
||||||
|
return Ok(TaskParamType::None);
|
||||||
|
}
|
||||||
|
// remove everything except the maintenancetool if repairing
|
||||||
|
for entry in entries {
|
||||||
|
let path = entry
|
||||||
|
.map_err(|e| format!("Error reading file: {}", e))?
|
||||||
|
.path();
|
||||||
|
if let Some(filename) = path.file_name() {
|
||||||
|
if filename.to_string_lossy().starts_with("maintenancetool") {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if path.is_dir() {
|
||||||
|
std::fs::remove_dir_all(&path)
|
||||||
|
.map_err(|e| format!("Error removing {}: {}", path.to_string_lossy(), e))?;
|
||||||
|
} else {
|
||||||
|
std::fs::remove_file(&path)
|
||||||
|
.map_err(|e| format!("Error removing {}: {}", path.to_string_lossy(), e))?;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(TaskParamType::None)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn dependencies(&self) -> Vec<TaskDependency> {
|
||||||
|
vec![]
|
||||||
|
}
|
||||||
|
|
||||||
|
fn name(&self) -> String {
|
||||||
|
"RemoveTargetDirTask".to_string()
|
||||||
|
}
|
||||||
|
}
|
|
@ -2,18 +2,18 @@
|
||||||
|
|
||||||
use std::env::consts::OS;
|
use std::env::consts::OS;
|
||||||
|
|
||||||
use installer::InstallerFramework;
|
use crate::installer::InstallerFramework;
|
||||||
|
|
||||||
use tasks::Task;
|
use crate::tasks::Task;
|
||||||
use tasks::TaskDependency;
|
use crate::tasks::TaskDependency;
|
||||||
use tasks::TaskMessage;
|
use crate::tasks::TaskMessage;
|
||||||
use tasks::TaskParamType;
|
use crate::tasks::TaskParamType;
|
||||||
|
|
||||||
use config::PackageDescription;
|
use crate::config::PackageDescription;
|
||||||
|
|
||||||
use regex::Regex;
|
use regex::Regex;
|
||||||
|
|
||||||
use logging::LoggingErrors;
|
use crate::logging::LoggingErrors;
|
||||||
|
|
||||||
pub struct ResolvePackageTask {
|
pub struct ResolvePackageTask {
|
||||||
pub name: String,
|
pub name: String,
|
||||||
|
|
|
@ -1,11 +1,11 @@
|
||||||
//! Saves the main database into the installation directory.
|
//! Saves the main database into the installation directory.
|
||||||
|
|
||||||
use installer::InstallerFramework;
|
use crate::installer::InstallerFramework;
|
||||||
|
|
||||||
use tasks::Task;
|
use crate::tasks::Task;
|
||||||
use tasks::TaskDependency;
|
use crate::tasks::TaskDependency;
|
||||||
use tasks::TaskMessage;
|
use crate::tasks::TaskMessage;
|
||||||
use tasks::TaskParamType;
|
use crate::tasks::TaskParamType;
|
||||||
|
|
||||||
pub struct SaveDatabaseTask {}
|
pub struct SaveDatabaseTask {}
|
||||||
|
|
||||||
|
|
|
@ -1,11 +1,11 @@
|
||||||
//! Saves the installer executable into the install directory.
|
//! Saves the installer executable into the install directory.
|
||||||
|
|
||||||
use installer::InstallerFramework;
|
use crate::installer::InstallerFramework;
|
||||||
|
|
||||||
use tasks::Task;
|
use crate::tasks::Task;
|
||||||
use tasks::TaskDependency;
|
use crate::tasks::TaskDependency;
|
||||||
use tasks::TaskMessage;
|
use crate::tasks::TaskMessage;
|
||||||
use tasks::TaskParamType;
|
use crate::tasks::TaskParamType;
|
||||||
|
|
||||||
use std::fs::File;
|
use std::fs::File;
|
||||||
use std::fs::OpenOptions;
|
use std::fs::OpenOptions;
|
||||||
|
@ -14,7 +14,7 @@ use std::io::copy;
|
||||||
|
|
||||||
use std::env::current_exe;
|
use std::env::current_exe;
|
||||||
|
|
||||||
use logging::LoggingErrors;
|
use crate::logging::LoggingErrors;
|
||||||
|
|
||||||
pub struct SaveExecutableTask {}
|
pub struct SaveExecutableTask {}
|
||||||
|
|
||||||
|
|
|
@ -1,14 +1,14 @@
|
||||||
//! Uninstalls a set of packages.
|
//! Uninstalls a set of packages.
|
||||||
|
|
||||||
use installer::InstallerFramework;
|
use crate::installer::InstallerFramework;
|
||||||
|
|
||||||
use tasks::Task;
|
use crate::tasks::Task;
|
||||||
use tasks::TaskParamType;
|
use crate::tasks::TaskParamType;
|
||||||
|
|
||||||
use tasks::uninstall_pkg::UninstallPackageTask;
|
use crate::tasks::uninstall_pkg::UninstallPackageTask;
|
||||||
use tasks::TaskDependency;
|
use crate::tasks::TaskDependency;
|
||||||
use tasks::TaskMessage;
|
use crate::tasks::TaskMessage;
|
||||||
use tasks::TaskOrdering;
|
use crate::tasks::TaskOrdering;
|
||||||
|
|
||||||
pub struct UninstallTask {
|
pub struct UninstallTask {
|
||||||
pub items: Vec<String>,
|
pub items: Vec<String>,
|
||||||
|
|
|
@ -1,15 +1,15 @@
|
||||||
//! Uninstalls a specific package.
|
//! Uninstalls a specific package.
|
||||||
|
|
||||||
use installer::InstallerFramework;
|
use crate::installer::InstallerFramework;
|
||||||
|
|
||||||
use tasks::Task;
|
use crate::tasks::Task;
|
||||||
use tasks::TaskDependency;
|
use crate::tasks::TaskDependency;
|
||||||
use tasks::TaskMessage;
|
use crate::tasks::TaskMessage;
|
||||||
use tasks::TaskParamType;
|
use crate::tasks::TaskParamType;
|
||||||
|
|
||||||
|
use crate::tasks::save_database::SaveDatabaseTask;
|
||||||
|
use crate::tasks::TaskOrdering;
|
||||||
use std::fs::remove_file;
|
use std::fs::remove_file;
|
||||||
use tasks::save_database::SaveDatabaseTask;
|
|
||||||
use tasks::TaskOrdering;
|
|
||||||
|
|
||||||
pub struct UninstallGlobalShortcutsTask {}
|
pub struct UninstallGlobalShortcutsTask {}
|
||||||
|
|
||||||
|
|
|
@ -1,21 +1,21 @@
|
||||||
//! Uninstalls a specific package.
|
//! Uninstalls a specific package.
|
||||||
|
|
||||||
use installer::InstallerFramework;
|
use crate::installer::InstallerFramework;
|
||||||
|
|
||||||
use tasks::save_database::SaveDatabaseTask;
|
use crate::tasks::save_database::SaveDatabaseTask;
|
||||||
use tasks::Task;
|
use crate::tasks::Task;
|
||||||
use tasks::TaskDependency;
|
use crate::tasks::TaskDependency;
|
||||||
use tasks::TaskMessage;
|
use crate::tasks::TaskMessage;
|
||||||
use tasks::TaskOrdering;
|
use crate::tasks::TaskOrdering;
|
||||||
use tasks::TaskParamType;
|
use crate::tasks::TaskParamType;
|
||||||
|
|
||||||
use installer::LocalInstallation;
|
use crate::installer::LocalInstallation;
|
||||||
|
|
||||||
use std::fs::remove_dir;
|
use std::fs::remove_dir;
|
||||||
use std::fs::remove_file;
|
use std::fs::remove_file;
|
||||||
|
|
||||||
use logging::LoggingErrors;
|
use crate::logging::LoggingErrors;
|
||||||
use tasks::uninstall_shortcuts::UninstallShortcutsTask;
|
use crate::tasks::uninstall_shortcuts::UninstallShortcutsTask;
|
||||||
|
|
||||||
pub struct UninstallPackageTask {
|
pub struct UninstallPackageTask {
|
||||||
pub name: String,
|
pub name: String,
|
||||||
|
|
|
@ -1,18 +1,18 @@
|
||||||
//! Uninstalls a specific package.
|
//! Uninstalls a specific package.
|
||||||
|
|
||||||
use installer::InstallerFramework;
|
use crate::installer::InstallerFramework;
|
||||||
|
|
||||||
use tasks::Task;
|
use crate::tasks::Task;
|
||||||
use tasks::TaskDependency;
|
use crate::tasks::TaskDependency;
|
||||||
use tasks::TaskMessage;
|
use crate::tasks::TaskMessage;
|
||||||
use tasks::TaskParamType;
|
use crate::tasks::TaskParamType;
|
||||||
|
|
||||||
use installer::LocalInstallation;
|
use crate::installer::LocalInstallation;
|
||||||
|
|
||||||
use std::fs::remove_dir;
|
use std::fs::remove_dir;
|
||||||
use std::fs::remove_file;
|
use std::fs::remove_file;
|
||||||
|
|
||||||
use logging::LoggingErrors;
|
use crate::logging::LoggingErrors;
|
||||||
|
|
||||||
pub struct UninstallShortcutsTask {
|
pub struct UninstallShortcutsTask {
|
||||||
pub name: String,
|
pub name: String,
|
||||||
|
|
|
@ -3,13 +3,15 @@ module.exports = {
|
||||||
env: {
|
env: {
|
||||||
node: true
|
node: true
|
||||||
},
|
},
|
||||||
'extends': [
|
extends: [
|
||||||
'plugin:vue/essential',
|
'plugin:vue/essential',
|
||||||
'@vue/standard'
|
'@vue/standard'
|
||||||
],
|
],
|
||||||
rules: {
|
rules: {
|
||||||
'no-console': process.env.NODE_ENV === 'production' ? 'error' : 'off',
|
'no-console': 'off',
|
||||||
'no-debugger': process.env.NODE_ENV === 'production' ? 'error' : 'off'
|
'no-debugger': process.env.NODE_ENV === 'production' ? 'error' : 'off',
|
||||||
|
'no-redeclare': 'off',
|
||||||
|
camelcase: 'off'
|
||||||
},
|
},
|
||||||
parserOptions: {
|
parserOptions: {
|
||||||
parser: 'babel-eslint'
|
parser: 'babel-eslint'
|
||||||
|
|
20
ui/merge-strings.js
Executable file
20
ui/merge-strings.js
Executable file
|
@ -0,0 +1,20 @@
|
||||||
|
#!/bin/env node
|
||||||
|
const fs = require('fs')
|
||||||
|
const merge = require('deepmerge')
|
||||||
|
const glob = require('glob')
|
||||||
|
|
||||||
|
glob('src/locales/!(messages).json', {}, (e, files) => {
|
||||||
|
let messages = []
|
||||||
|
for (const file of files) {
|
||||||
|
console.log(`Loading ${file}...`)
|
||||||
|
const locale_messages = require(`./${file}`)
|
||||||
|
messages.push(locale_messages)
|
||||||
|
}
|
||||||
|
console.log('Merging messages...')
|
||||||
|
if (messages && messages.length > 1) {
|
||||||
|
messages = merge.all(messages)
|
||||||
|
} else {
|
||||||
|
messages = messages[0] // single locale mode
|
||||||
|
}
|
||||||
|
fs.writeFileSync('src/locales/messages.json', JSON.stringify(messages), {})
|
||||||
|
})
|
|
@ -4,7 +4,20 @@ const express = require('express')
|
||||||
const app = express()
|
const app = express()
|
||||||
const port = 3000
|
const port = 3000
|
||||||
|
|
||||||
|
let showError = false
|
||||||
|
let showConfigError = false
|
||||||
|
let maintenance = false
|
||||||
|
let launcher = false
|
||||||
|
let fileExists = false
|
||||||
|
let darkMode = false
|
||||||
|
|
||||||
function progressSimulation (res) {
|
function progressSimulation (res) {
|
||||||
|
if (showError) {
|
||||||
|
var resp = JSON.stringify({ Error: 'Simulated error.' }) + '\n'
|
||||||
|
res.write(resp)
|
||||||
|
res.status(200).end()
|
||||||
|
return
|
||||||
|
}
|
||||||
var progress = 0.0
|
var progress = 0.0
|
||||||
var timer = setInterval(() => {
|
var timer = setInterval(() => {
|
||||||
var resp = JSON.stringify({ Status: ['Processing...', progress] }) + '\n'
|
var resp = JSON.stringify({ Status: ['Processing...', progress] }) + '\n'
|
||||||
|
@ -14,10 +27,14 @@ function progressSimulation (res) {
|
||||||
res.status(200).end()
|
res.status(200).end()
|
||||||
clearInterval(timer)
|
clearInterval(timer)
|
||||||
}
|
}
|
||||||
}, 1500)
|
}, 500)
|
||||||
}
|
}
|
||||||
|
|
||||||
function returnConfig (res) {
|
function returnConfig (res) {
|
||||||
|
if (showConfigError) {
|
||||||
|
res.status(500).json({})
|
||||||
|
return
|
||||||
|
}
|
||||||
res.json({
|
res.json({
|
||||||
installing_message:
|
installing_message:
|
||||||
'Test Banner <strong>Bold</strong> <pre>Code block</pre> <i>Italic</i> <del>Strike</del>',
|
'Test Banner <strong>Bold</strong> <pre>Code block</pre> <i>Italic</i> <del>Strike</del>',
|
||||||
|
@ -52,21 +69,22 @@ function returnConfig (res) {
|
||||||
}
|
}
|
||||||
|
|
||||||
app.get('/api/attrs', (req, res) => {
|
app.get('/api/attrs', (req, res) => {
|
||||||
|
console.log('-- Get attrs')
|
||||||
res.send(
|
res.send(
|
||||||
`var base_attributes = {"name":"yuzu","target_url":"https://raw.githubusercontent.com/j-selby/test-installer/master/config.linux.v2.toml"};`
|
{ name: 'yuzu', target_url: 'https://raw.githubusercontent.com/j-selby/test-installer/master/config.linux.v2.toml' }
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
|
|
||||||
app.get('/api/dark-mode', (req, res) => {
|
app.get('/api/dark-mode', (req, res) => {
|
||||||
res.json(false)
|
res.json(darkMode)
|
||||||
})
|
})
|
||||||
|
|
||||||
app.get('/api/installation-status', (req, res) => {
|
app.get('/api/installation-status', (req, res) => {
|
||||||
res.json({
|
res.json({
|
||||||
database: { packages: [], shortcuts: [] },
|
database: { packages: [], shortcuts: [] },
|
||||||
install_path: null,
|
install_path: null,
|
||||||
preexisting_install: false,
|
preexisting_install: maintenance,
|
||||||
is_launcher: false,
|
is_launcher: launcher,
|
||||||
launcher_path: null
|
launcher_path: null
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
@ -82,13 +100,54 @@ app.get('/api/config', (req, res) => {
|
||||||
})
|
})
|
||||||
|
|
||||||
app.post('/api/start-install', (req, res) => {
|
app.post('/api/start-install', (req, res) => {
|
||||||
console.log(`-- Install: ${req.body}`)
|
console.log(`-- Install: ${req}`)
|
||||||
progressSimulation(res)
|
progressSimulation(res)
|
||||||
})
|
})
|
||||||
|
|
||||||
app.get('/api/exit', (req, res) => {
|
app.get('/api/exit', (req, res) => {
|
||||||
console.log('-- Exit')
|
console.log('-- Exit')
|
||||||
res.status(204)
|
if (showError) {
|
||||||
|
res.status(500).send('Simulated error: Nothing to see here.')
|
||||||
|
return
|
||||||
|
}
|
||||||
|
res.status(204).send()
|
||||||
|
})
|
||||||
|
|
||||||
|
app.post('/api/verify-path', (req, res) => {
|
||||||
|
console.log('-- Verify Path')
|
||||||
|
res.send({
|
||||||
|
exists: fileExists
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
process.argv.forEach((val, index) => {
|
||||||
|
switch (val) {
|
||||||
|
case 'maintenance':
|
||||||
|
maintenance = true
|
||||||
|
console.log('Simulating maintenance mode')
|
||||||
|
break
|
||||||
|
case 'launcher':
|
||||||
|
maintenance = true
|
||||||
|
launcher = true
|
||||||
|
console.log('Simulating launcher mode')
|
||||||
|
break
|
||||||
|
case 'exists':
|
||||||
|
fileExists = true
|
||||||
|
console.log('Simulating file exists situation')
|
||||||
|
break
|
||||||
|
case 'dark':
|
||||||
|
darkMode = true
|
||||||
|
console.log('Simulating dark mode')
|
||||||
|
break
|
||||||
|
case 'config-error':
|
||||||
|
showConfigError = true
|
||||||
|
console.log('Simulating configuration errors')
|
||||||
|
break
|
||||||
|
case 'error':
|
||||||
|
showError = true
|
||||||
|
console.log('Simulating errors')
|
||||||
|
break
|
||||||
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
console.log(`Listening on ${port}...`)
|
console.log(`Listening on ${port}...`)
|
||||||
|
|
|
@ -5,23 +5,32 @@
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"serve": "vue-cli-service serve",
|
"serve": "vue-cli-service serve",
|
||||||
"build": "vue-cli-service build",
|
"build": "vue-cli-service build",
|
||||||
"lint": "vue-cli-service lint"
|
"lint": "vue-cli-service lint",
|
||||||
|
"postinstall": "node merge-strings.js"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"buefy": "^0.7.7",
|
"@mdi/font": "^5.9.55",
|
||||||
"vue": "^2.6.6",
|
"axios": "^0.21.1",
|
||||||
"vue-router": "^3.0.1"
|
"buefy": "^0.9.7",
|
||||||
|
"vue": "^2.6.14",
|
||||||
|
"vue-axios": "^3.2.4",
|
||||||
|
"vue-i18n": "^8.24.4",
|
||||||
|
"vue-router": "^3.5.1"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@vue/cli-plugin-babel": "^3.5.0",
|
"@vue/cli-plugin-babel": "^4.5.13",
|
||||||
"@vue/cli-plugin-eslint": "^3.5.0",
|
"@vue/cli-plugin-eslint": "^4.5.13",
|
||||||
"@vue/cli-service": "^3.5.0",
|
"@vue/cli-service": "^4.5.13",
|
||||||
"@vue/eslint-config-standard": "^4.0.0",
|
"@vue/eslint-config-standard": "^6.0.0",
|
||||||
"babel-eslint": "^10.0.1",
|
"babel-eslint": "^10.1.0",
|
||||||
"eslint": "^5.8.0",
|
"eslint": "^7.28.0",
|
||||||
"eslint-plugin-vue": "^5.0.0",
|
"eslint-plugin-import": "^2.23.4",
|
||||||
|
"eslint-plugin-node": "^11.1.0",
|
||||||
|
"eslint-plugin-promise": "^5.1.0",
|
||||||
|
"eslint-plugin-standard": "^4.1.0",
|
||||||
|
"eslint-plugin-vue": "^7.10.0",
|
||||||
"express": "^4.17.1",
|
"express": "^4.17.1",
|
||||||
"http-proxy-middleware": "^0.19.1",
|
"http-proxy-middleware": "^2.0.0",
|
||||||
"vue-template-compiler": "^2.5.21"
|
"vue-template-compiler": "^2.6.14"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Binary file not shown.
Before Width: | Height: | Size: 105 KiB After Width: | Height: | Size: 15 KiB |
|
@ -4,7 +4,6 @@
|
||||||
<meta charset="utf-8">
|
<meta charset="utf-8">
|
||||||
<meta http-equiv="X-UA-Compatible" content="IE=11">
|
<meta http-equiv="X-UA-Compatible" content="IE=11">
|
||||||
<meta name="viewport" content="width=device-width,initial-scale=1.0">
|
<meta name="viewport" content="width=device-width,initial-scale=1.0">
|
||||||
<script src="/api/attrs" type="text/javascript"></script>
|
|
||||||
<link rel="icon" href="<%= BASE_URL %>favicon.ico">
|
<link rel="icon" href="<%= BASE_URL %>favicon.ico">
|
||||||
<title id="window-title">... Installer</title>
|
<title id="window-title">... Installer</title>
|
||||||
</head>
|
</head>
|
||||||
|
|
|
@ -9,15 +9,23 @@
|
||||||
<br />
|
<br />
|
||||||
|
|
||||||
<h2 class="subtitle" v-if="!$root.$data.metadata.preexisting_install">
|
<h2 class="subtitle" v-if="!$root.$data.metadata.preexisting_install">
|
||||||
Welcome to the {{ $root.$data.attrs.name }} installer!
|
{{ $t('app.installer_title', {'name': $root.$data.attrs.name}) }}
|
||||||
</h2>
|
</h2>
|
||||||
<h2 class="subtitle" v-if="!$root.$data.metadata.preexisting_install">
|
<h2 class="subtitle" v-if="!$root.$data.metadata.preexisting_install">
|
||||||
We will have you up and running in just a few moments.
|
{{ $t('app.installer_subtitle') }}
|
||||||
</h2>
|
</h2>
|
||||||
|
|
||||||
<h2 class="subtitle" v-if="$root.$data.metadata.preexisting_install">
|
<h2 class="subtitle" v-if="$root.$data.metadata.preexisting_install">
|
||||||
Welcome to the {{ $root.$data.attrs.name }} Maintenance Tool.
|
{{ $t('app.maintenance_title', {'name': $root.$data.attrs.name}) }}
|
||||||
</h2>
|
</h2>
|
||||||
|
<b-dropdown hoverable @change="selectLocale" aria-role="list">
|
||||||
|
<button class="button" slot="trigger">
|
||||||
|
<span>{{ $t('locale') }}</span>
|
||||||
|
<b-icon icon="menu-down"></b-icon>
|
||||||
|
</button>
|
||||||
|
|
||||||
|
<b-dropdown-item v-for="(locale, index) in this.$i18n.messages" v-bind:key="index" :value="index" aria-role="listitem">{{locale.locale}}</b-dropdown-item>
|
||||||
|
</b-dropdown>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<router-view />
|
<router-view />
|
||||||
|
@ -27,6 +35,33 @@
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
export default {
|
||||||
|
mounted: function () {
|
||||||
|
// detect languages
|
||||||
|
const languages = window.navigator.languages
|
||||||
|
if (languages) {
|
||||||
|
// standard-compliant browsers
|
||||||
|
for (let index = 0; index < languages.length; index++) {
|
||||||
|
const lang = languages[index]
|
||||||
|
// Find the most preferred language that we support
|
||||||
|
if (Object.prototype.hasOwnProperty.call(this.$i18n.messages, lang)) {
|
||||||
|
this.$i18n.locale = lang
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// IE9+ support
|
||||||
|
this.$i18n.locale = window.navigator.browserLanguage
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
selectLocale: function (locale) {
|
||||||
|
this.$i18n.locale = locale
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
<style>
|
<style>
|
||||||
/* roboto-regular - latin */
|
/* roboto-regular - latin */
|
||||||
@font-face {
|
@font-face {
|
||||||
|
@ -148,7 +183,7 @@ pre {
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Dark mode */
|
/* Dark mode */
|
||||||
body.has-background-black-ter .subtitle, body.has-background-black-ter .column > div, body.has-background-black-ter section {
|
body.has-background-black-ter .subtitle, body.has-background-black-ter .column > div {
|
||||||
color: hsl(0, 0%, 96%);
|
color: hsl(0, 0%, 96%);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
BIN
ui/src/assets/logo.png
Normal file
BIN
ui/src/assets/logo.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 6.2 KiB |
|
@ -4,60 +4,6 @@
|
||||||
* Additional state-less helper methods.
|
* Additional state-less helper methods.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
var request_id = 0
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Makes a AJAX request.
|
|
||||||
*
|
|
||||||
* @param path The path to connect to.
|
|
||||||
* @param successCallback A callback with a JSON payload.
|
|
||||||
* @param failCallback A fail callback. Optional.
|
|
||||||
* @param data POST data. Optional.
|
|
||||||
*/
|
|
||||||
export function ajax (path, successCallback, failCallback, data) {
|
|
||||||
if (failCallback === undefined) {
|
|
||||||
failCallback = defaultFailHandler
|
|
||||||
}
|
|
||||||
|
|
||||||
console.log('Making HTTP request to ' + path)
|
|
||||||
|
|
||||||
var req = new XMLHttpRequest()
|
|
||||||
|
|
||||||
req.addEventListener('load', function () {
|
|
||||||
// The server can sometimes return a string error. Make sure we handle this.
|
|
||||||
if (this.status === 200 && this.getResponseHeader('Content-Type').indexOf('application/json') !== -1) {
|
|
||||||
successCallback(JSON.parse(this.responseText))
|
|
||||||
} else {
|
|
||||||
failCallback(this.responseText)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
req.addEventListener('error', failCallback)
|
|
||||||
|
|
||||||
req.open(data == null ? 'GET' : 'POST', path + '?nocache=' + request_id++, true)
|
|
||||||
// Rocket only currently supports URL encoded forms.
|
|
||||||
req.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded')
|
|
||||||
|
|
||||||
if (data != null) {
|
|
||||||
var form = ''
|
|
||||||
|
|
||||||
for (var key in data) {
|
|
||||||
if (!data.hasOwnProperty(key)) {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
if (form !== '') {
|
|
||||||
form += '&'
|
|
||||||
}
|
|
||||||
|
|
||||||
form += encodeURIComponent(key) + '=' + encodeURIComponent(data[key])
|
|
||||||
}
|
|
||||||
|
|
||||||
req.send(form)
|
|
||||||
} else {
|
|
||||||
req.send()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Makes a AJAX request, streaming each line as it arrives. Type should be text/plain,
|
* Makes a AJAX request, streaming each line as it arrives. Type should be text/plain,
|
||||||
* each line will be interpreted as JSON separately.
|
* each line will be interpreted as JSON separately.
|
||||||
|
@ -108,7 +54,7 @@ export function stream_ajax (path, callback, successCallback, failCallback, data
|
||||||
|
|
||||||
req.addEventListener('error', failCallback)
|
req.addEventListener('error', failCallback)
|
||||||
|
|
||||||
req.open(data == null ? 'GET' : 'POST', path + '?nocache=' + request_id++, true)
|
req.open(data == null ? 'GET' : 'POST', path + '?nocache=' + Date.now(), true)
|
||||||
// Rocket only currently supports URL encoded forms.
|
// Rocket only currently supports URL encoded forms.
|
||||||
req.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded')
|
req.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded')
|
||||||
|
|
||||||
|
@ -116,7 +62,7 @@ export function stream_ajax (path, callback, successCallback, failCallback, data
|
||||||
var form = ''
|
var form = ''
|
||||||
|
|
||||||
for (var key in data) {
|
for (var key in data) {
|
||||||
if (!data.hasOwnProperty(key)) {
|
if (!data[key]) {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -132,13 +78,3 @@ export function stream_ajax (path, callback, successCallback, failCallback, data
|
||||||
req.send()
|
req.send()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* The default handler if a AJAX request fails. Not to be used directly.
|
|
||||||
*
|
|
||||||
* @param e The XMLHttpRequest that failed.
|
|
||||||
*/
|
|
||||||
function defaultFailHandler (e) {
|
|
||||||
console.error('A AJAX request failed, and was not caught:')
|
|
||||||
console.error(e)
|
|
||||||
}
|
|
||||||
|
|
1
ui/src/locales/.gitignore
vendored
Normal file
1
ui/src/locales/.gitignore
vendored
Normal file
|
@ -0,0 +1 @@
|
||||||
|
messages.json
|
66
ui/src/locales/en.json
Normal file
66
ui/src/locales/en.json
Normal file
|
@ -0,0 +1,66 @@
|
||||||
|
{
|
||||||
|
"en":{
|
||||||
|
"locale":"English",
|
||||||
|
"app":{
|
||||||
|
"installer_title":"Welcome to the {name} installer!",
|
||||||
|
"installer_subtitle":"We will have you up and running in just a few moments.",
|
||||||
|
"maintenance_title":"Welcome to the {name} Maintenance Tool.",
|
||||||
|
"window_title":"{name} Installer"
|
||||||
|
},
|
||||||
|
"download_config":{
|
||||||
|
"download_config":"Downloading config...",
|
||||||
|
"error_download_config":"Got error while downloading config: {msg}"
|
||||||
|
},
|
||||||
|
"select_packages":{
|
||||||
|
"title":"Select which packages you want to install:",
|
||||||
|
"title_repair":"Select which packages you want to repair:",
|
||||||
|
"installed":"(installed)",
|
||||||
|
"advanced":"Advanced...",
|
||||||
|
"install":"Install",
|
||||||
|
"modify":"Modify",
|
||||||
|
"repair": "Repair",
|
||||||
|
"location":"Install Location",
|
||||||
|
"location_placeholder":"Enter a install path here",
|
||||||
|
"select":"Select",
|
||||||
|
"overwriting": "Overwriting",
|
||||||
|
"overwriting_warning": "Directory {path} already exists.<br>Are you sure you want to <b>overwrite</b> the contents inside?",
|
||||||
|
"nothing_picked": "Nothing selected",
|
||||||
|
"nothing_picked_warning": "Please select at least one package to install!"
|
||||||
|
},
|
||||||
|
"install_packages":{
|
||||||
|
"check_for_update":"Checking for updates...",
|
||||||
|
"uninstall":"Uninstalling...",
|
||||||
|
"self_update":"Downloading self-update...",
|
||||||
|
"install":"Installing...",
|
||||||
|
"please_wait":"Please wait..."
|
||||||
|
},
|
||||||
|
"error":{
|
||||||
|
"title":"An error occurred",
|
||||||
|
"exit_error":"{msg}\n\nPlease upload the log file (in {path}) to the {name} team",
|
||||||
|
"location_unknown":"the location where this installer is"
|
||||||
|
},
|
||||||
|
"complete":{
|
||||||
|
"thanks":"Thanks for installing {name}!",
|
||||||
|
"up_to_date":"{name} is already up to date!",
|
||||||
|
"updated":"{name} has been updated.",
|
||||||
|
"uninstalled":"{name} has been uninstalled.",
|
||||||
|
"where_to_find":"You can find your installed applications in your start menu."
|
||||||
|
},
|
||||||
|
"modify":{
|
||||||
|
"title":"Choose an option:",
|
||||||
|
"update":"Update",
|
||||||
|
"modify":"Modify",
|
||||||
|
"repair": "Repair",
|
||||||
|
"uninstall":"Uninstall",
|
||||||
|
"view_local_files": "View local files",
|
||||||
|
"prompt":"Are you sure you want to uninstall {name}?",
|
||||||
|
"prompt_confirm":"Uninstall {name}"
|
||||||
|
},
|
||||||
|
"back":"Back",
|
||||||
|
"exit":"Exit",
|
||||||
|
"yes":"Yes",
|
||||||
|
"no":"No",
|
||||||
|
"continue": "Continue",
|
||||||
|
"cancel":"Cancel"
|
||||||
|
}
|
||||||
|
}
|
119
ui/src/main.js
119
ui/src/main.js
|
@ -1,17 +1,30 @@
|
||||||
import Vue from 'vue'
|
import Vue from 'vue'
|
||||||
import App from './App.vue'
|
import App from './App.vue'
|
||||||
import router from './router'
|
import router from './router'
|
||||||
import { ajax, stream_ajax } from './helpers'
|
import axios from 'axios'
|
||||||
|
import VueAxios from 'vue-axios'
|
||||||
|
import VueI18n from 'vue-i18n'
|
||||||
|
import { stream_ajax as streamAjax } from './helpers'
|
||||||
import Buefy from 'buefy'
|
import Buefy from 'buefy'
|
||||||
|
import messages from './locales/messages.json'
|
||||||
import 'buefy/dist/buefy.css'
|
import 'buefy/dist/buefy.css'
|
||||||
|
import '@mdi/font/css/materialdesignicons.min.css'
|
||||||
|
|
||||||
Vue.config.productionTip = false
|
Vue.config.productionTip = false
|
||||||
Vue.use(Buefy)
|
Vue.use(Buefy)
|
||||||
|
Vue.use(VueI18n)
|
||||||
|
Vue.use(VueAxios, axios)
|
||||||
|
|
||||||
|
export const i18n = new VueI18n({
|
||||||
|
locale: 'en', // set locale
|
||||||
|
fallbackLocale: 'en',
|
||||||
|
messages // set locale messages
|
||||||
|
})
|
||||||
|
|
||||||
// Borrowed from http://tobyho.com/2012/07/27/taking-over-console-log/
|
// Borrowed from http://tobyho.com/2012/07/27/taking-over-console-log/
|
||||||
function intercept (method) {
|
function intercept (method) {
|
||||||
console[method] = function () {
|
console[method] = function () {
|
||||||
var message = Array.prototype.slice.apply(arguments).join(' ')
|
const message = Array.prototype.slice.apply(arguments).join(' ')
|
||||||
window.external.invoke(
|
window.external.invoke(
|
||||||
JSON.stringify({
|
JSON.stringify({
|
||||||
Log: {
|
Log: {
|
||||||
|
@ -24,18 +37,18 @@ function intercept (method) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// See if we have access to the JSON interface
|
// See if we have access to the JSON interface
|
||||||
var has_external_interface = false;
|
let hasExternalInterface = false
|
||||||
try {
|
try {
|
||||||
window.external.invoke(JSON.stringify({
|
window.external.invoke(JSON.stringify({
|
||||||
Test: {}
|
Test: {}
|
||||||
}))
|
}))
|
||||||
has_external_interface = true;
|
hasExternalInterface = true
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.warn("Running without JSON interface - unexpected behaviour may occur!")
|
console.warn('Running without JSON interface - unexpected behaviour may occur!')
|
||||||
}
|
}
|
||||||
|
|
||||||
// Overwrite loggers with the logging backend
|
// Overwrite loggers with the logging backend
|
||||||
if (has_external_interface) {
|
if (hasExternalInterface) {
|
||||||
window.onerror = function (msg, url, line) {
|
window.onerror = function (msg, url, line) {
|
||||||
window.external.invoke(
|
window.external.invoke(
|
||||||
JSON.stringify({
|
JSON.stringify({
|
||||||
|
@ -47,14 +60,14 @@ if (has_external_interface) {
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
var methods = ['log', 'warn', 'error']
|
const methods = ['log', 'warn', 'error']
|
||||||
for (var i = 0; i < methods.length; i++) {
|
for (let i = 0; i < methods.length; i++) {
|
||||||
intercept(methods[i])
|
intercept(methods[i])
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Disable F5
|
// Disable F5
|
||||||
function disable_shortcuts (e) {
|
function disableShortcuts (e) {
|
||||||
switch (e.keyCode) {
|
switch (e.keyCode) {
|
||||||
case 116: // F5
|
case 116: // F5
|
||||||
e.preventDefault()
|
e.preventDefault()
|
||||||
|
@ -63,25 +76,32 @@ function disable_shortcuts (e) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check to see if we need to enable dark mode
|
// Check to see if we need to enable dark mode
|
||||||
ajax('/api/dark-mode', function (enable) {
|
axios.get('/api/dark-mode').then(function (resp) {
|
||||||
if (enable) {
|
if (resp.data === true) {
|
||||||
document.body.classList.add('has-background-black-ter')
|
document.body.classList.add('has-background-black-ter')
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
window.addEventListener('keydown', disable_shortcuts)
|
window.addEventListener('keydown', disableShortcuts)
|
||||||
|
|
||||||
document.getElementById('window-title').innerText =
|
axios.get('/api/attrs').then(function (resp) {
|
||||||
base_attributes.name + ' Installer'
|
document.getElementById('window-title').innerText =
|
||||||
|
i18n.t('app.window_title', { name: resp.data.name })
|
||||||
|
}).catch(function (err) {
|
||||||
|
console.error(err)
|
||||||
|
})
|
||||||
|
|
||||||
function selectFileCallback (name) {
|
function selectFileCallback (name) {
|
||||||
app.install_location = name
|
app.install_location = name
|
||||||
}
|
}
|
||||||
|
|
||||||
var app = new Vue({
|
window.selectFileCallback = selectFileCallback
|
||||||
|
|
||||||
|
const app = new Vue({
|
||||||
|
i18n: i18n,
|
||||||
router: router,
|
router: router,
|
||||||
data: {
|
data: {
|
||||||
attrs: base_attributes,
|
attrs: {},
|
||||||
config: {},
|
config: {},
|
||||||
install_location: '',
|
install_location: '',
|
||||||
username: '',
|
username: '',
|
||||||
|
@ -102,54 +122,61 @@ var app = new Vue({
|
||||||
render: function (caller) {
|
render: function (caller) {
|
||||||
return caller(App)
|
return caller(App)
|
||||||
},
|
},
|
||||||
|
mounted: function () {
|
||||||
|
axios.get('/api/attrs').then(function (resp) {
|
||||||
|
app.attrs = resp.data
|
||||||
|
}).catch(function (err) {
|
||||||
|
console.error(err)
|
||||||
|
})
|
||||||
|
},
|
||||||
methods: {
|
methods: {
|
||||||
exit: function () {
|
exit: function () {
|
||||||
ajax(
|
axios.get('/api/exit').catch(function (msg) {
|
||||||
'/api/exit',
|
const searchLocation = (app.metadata.install_path && app.metadata.install_path.length > 0)
|
||||||
function () {},
|
? app.metadata.install_path
|
||||||
function (msg) {
|
: i18n.t('error.location_unknown')
|
||||||
var search_location = app.metadata.install_path.length > 0 ? app.metadata.install_path :
|
|
||||||
"the location where this installer is";
|
|
||||||
|
|
||||||
app.$router.replace({ name: 'showerr', params: { msg: msg +
|
app.$router.replace({
|
||||||
'\n\nPlease upload the log file (in ' + search_location + ') to ' +
|
name: 'showerr',
|
||||||
'the ' + app.attrs.name + ' team'
|
params: {
|
||||||
}});
|
msg: i18n.t('error.exit_error', {
|
||||||
},
|
name: app.attrs.name,
|
||||||
{} // pass in nothing to cause `ajax` to post instead of get
|
path: searchLocation,
|
||||||
)
|
msg: msg
|
||||||
|
})
|
||||||
|
}
|
||||||
|
})
|
||||||
|
})
|
||||||
},
|
},
|
||||||
check_authentication: function (success, error) {
|
check_authentication: function (success, error) {
|
||||||
var that = this;
|
const that = this
|
||||||
var app = this.$root;
|
const app = this.$root
|
||||||
|
|
||||||
app.ajax('/api/check-auth', function (auth) {
|
app.ajax('/api/check-auth', function (auth) {
|
||||||
app.$data.username = auth.username;
|
app.$data.username = auth.username
|
||||||
app.$data.token = auth.token;
|
app.$data.token = auth.token
|
||||||
that.jwt_token = auth.jwt_token;
|
that.jwt_token = auth.jwt_token
|
||||||
that.is_authenticated = Object.keys(that.jwt_token).length !== 0 && that.jwt_token.constructor === Object;
|
that.is_authenticated = Object.keys(that.jwt_token).length !== 0 && that.jwt_token.constructor === Object
|
||||||
if (that.is_authenticated) {
|
if (that.is_authenticated) {
|
||||||
// Give all permissions to vip roles
|
// Give all permissions to vip roles
|
||||||
that.is_linked = that.jwt_token.isPatreonAccountLinked;
|
that.is_linked = that.jwt_token.isPatreonAccountLinked
|
||||||
that.is_subscribed = that.jwt_token.isPatreonSubscriptionActive;
|
that.is_subscribed = that.jwt_token.isPatreonSubscriptionActive
|
||||||
that.has_reward_tier = that.jwt_token.releaseChannels.indexOf("early-access") > -1;
|
that.has_reward_tier = that.jwt_token.releaseChannels.indexOf('early-access') > -1
|
||||||
}
|
}
|
||||||
if (success) {
|
if (success) {
|
||||||
success();
|
success()
|
||||||
}
|
}
|
||||||
}, function (e) {
|
}, function (e) {
|
||||||
if (error) {
|
if (error) {
|
||||||
error();
|
error()
|
||||||
}
|
}
|
||||||
}, {
|
}, {
|
||||||
"username": app.$data.username,
|
username: app.$data.username,
|
||||||
"token": app.$data.token
|
token: app.$data.token
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
|
stream_ajax: streamAjax
|
||||||
ajax: ajax,
|
|
||||||
stream_ajax: stream_ajax
|
|
||||||
}
|
}
|
||||||
}).$mount('#app')
|
}).$mount('#app')
|
||||||
|
|
||||||
console.log("Vue started")
|
console.log('Vue started')
|
||||||
|
|
|
@ -1,40 +1,40 @@
|
||||||
<template>
|
<template>
|
||||||
<div class="column has-padding">
|
<div class="column has-padding">
|
||||||
<div v-if="was_migrate">
|
<div v-if="was_migrate">
|
||||||
<h4 class="subtitle">You have been moved to the new, single version of {{ $root.$data.attrs.name }}.</h4>
|
<h4 class="subtitle">You have been moved to the new, single version of {{ $root.$data.attrs.name }}.</h4>
|
||||||
|
|
||||||
<p>You can find your installed applications in your start menu - if you were in the middle of something, just reattempt.</p>
|
<p>You can find your installed applications in your start menu - if you were in the middle of something, just reattempt.</p>
|
||||||
|
|
||||||
<img src="../assets/how-to-open.png" alt="Where yuzu is installed"/>
|
<img src="../assets/how-to-open.png" alt="Where yuzu is installed"/>
|
||||||
</div>
|
</div>
|
||||||
<div v-else-if="was_update">
|
<div v-else-if="was_update">
|
||||||
<div v-if="has_installed">
|
<div v-if="has_installed">
|
||||||
<h4 class="subtitle">{{ $root.$data.attrs.name }} has been updated.</h4>
|
<h4 class="subtitle">{{ $t('complete.updated', {'name': $root.$data.attrs.name}) }}</h4>
|
||||||
|
|
||||||
<p>You can find your installed applications in your start menu.</p>
|
<p>{{ $t('complete.where_to_find') }}</p>
|
||||||
|
</div>
|
||||||
|
<div v-else>
|
||||||
|
<h4 class="subtitle">{{ $t('complete.up_to_date', {'name': $root.$data.attrs.name}) }}</h4>
|
||||||
|
|
||||||
|
<p>{{ $t('complete.where_to_find') }}</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div v-else-if="was_install">
|
||||||
|
<h4 class="subtitle">{{ $t('complete.thanks', {'name': $root.$data.attrs.name}) }}</h4>
|
||||||
|
|
||||||
|
<p>{{ $t('complete.where_to_find') }}</p>
|
||||||
|
<br>
|
||||||
|
<img src="../assets/how-to-open.png" alt="Where yuzu is installed"/>
|
||||||
</div>
|
</div>
|
||||||
<div v-else>
|
<div v-else>
|
||||||
<h4 class="subtitle">{{ $root.$data.attrs.name }} is already up to date!</h4>
|
<h4 class="subtitle">{{ $t('complete.uninstalled', {'name': $root.$data.attrs.name}) }}</h4>
|
||||||
|
|
||||||
<p>You can find your installed applications in your start menu.</p>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
|
||||||
<div v-else-if="was_install">
|
|
||||||
<h4 class="subtitle">Thanks for installing {{ $root.$data.attrs.name }}!</h4>
|
|
||||||
|
|
||||||
<p>You can find your installed applications in your start menu.</p>
|
<div class="field is-grouped is-right-floating is-bottom-floating">
|
||||||
<br>
|
<p class="control">
|
||||||
<img src="../assets/how-to-open.png" alt="Where yuzu is installed"/>
|
<b-button class="is-dark is-medium" v-on:click="exit">{{ $t('exit') }}</b-button>
|
||||||
</div>
|
</p>
|
||||||
<div v-else>
|
</div>
|
||||||
<h4 class="subtitle">{{ $root.$data.attrs.name }} has been uninstalled.</h4>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="field is-grouped is-right-floating is-bottom-floating">
|
|
||||||
<p class="control">
|
|
||||||
<a class="button is-dark is-medium" v-on:click="exit">Exit</a>
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
|
@ -46,12 +46,12 @@ export default {
|
||||||
was_install: !this.$route.params.uninstall,
|
was_install: !this.$route.params.uninstall,
|
||||||
was_update: this.$route.params.update,
|
was_update: this.$route.params.update,
|
||||||
was_migrate: this.$route.params.migrate,
|
was_migrate: this.$route.params.migrate,
|
||||||
has_installed: this.$route.params.packages_installed > 0,
|
has_installed: this.$route.params.packages_installed > 0
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
exit: function () {
|
exit: function () {
|
||||||
this.$root.exit();
|
this.$root.exit()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
<template>
|
<template>
|
||||||
<div class="column has-padding">
|
<div class="column has-padding">
|
||||||
<h4 class="subtitle">Downloading config...</h4>
|
<h4 class="subtitle">{{ $t('download_config.download_config') }}</h4>
|
||||||
|
|
||||||
<br />
|
<br />
|
||||||
<progress class="progress is-info is-medium" max="100">
|
<progress class="progress is-info is-medium" max="100">
|
||||||
|
@ -17,51 +17,54 @@ export default {
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
download_install_status: function () {
|
download_install_status: function () {
|
||||||
var that = this
|
const that = this
|
||||||
this.$root.ajax('/api/installation-status', function (e) {
|
this.$http.get('/api/installation-status').then(function (resp) {
|
||||||
that.$root.metadata = e
|
that.$root.metadata = resp.data
|
||||||
|
|
||||||
that.download_config()
|
that.download_config()
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
download_config: function () {
|
download_config: function () {
|
||||||
var that = this
|
const that = this
|
||||||
this.$root.ajax('/api/config', function (e) {
|
this.$http.get('/api/config').then(function (resp) {
|
||||||
that.$root.config = e
|
that.$root.config = resp.data
|
||||||
|
|
||||||
// Update the updater if needed
|
|
||||||
if (that.$root.config.new_tool) {
|
|
||||||
that.$router.push('/install/updater/false')
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
that.$root.check_authentication(that.choose_next_state, that.choose_next_state)
|
that.$root.check_authentication(that.choose_next_state, that.choose_next_state)
|
||||||
}, function (e) {
|
}).catch(function (e) {
|
||||||
console.error('Got error while downloading config: ' + e)
|
console.error('Got error while downloading config: ' +
|
||||||
|
e)
|
||||||
|
|
||||||
if (that.$root.metadata.is_launcher) {
|
if (that.$root.metadata.is_launcher) {
|
||||||
// Just launch the target application
|
// Just launch the target application
|
||||||
that.$root.exit()
|
that.$root.exit()
|
||||||
} else {
|
} else {
|
||||||
that.$router.replace({ name: 'showerr',
|
that.$router.replace({
|
||||||
params: { msg: 'Got error while downloading config: ' + e } })
|
name: 'showerr',
|
||||||
|
params: { msg: that.$i18n.t('download_config.error_download_config', { msg: e }) }
|
||||||
|
})
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
choose_next_state: function () {
|
choose_next_state: function () {
|
||||||
var app = this.$root
|
const app = this.$root
|
||||||
|
// Update the updater if needed
|
||||||
|
if (app.config.new_tool) {
|
||||||
|
this.$router.push('/install/updater/false')
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
if (app.metadata.preexisting_install) {
|
if (app.metadata.preexisting_install) {
|
||||||
app.install_location = app.metadata.install_path
|
app.install_location = app.metadata.install_path
|
||||||
|
|
||||||
// Copy over installed packages
|
// Copy over installed packages
|
||||||
for (var x = 0; x < app.config.packages.length; x++) {
|
for (let x = 0; x < app.config.packages.length; x++) {
|
||||||
app.config.packages[x].default = false
|
app.config.packages[x].default = false
|
||||||
app.config.packages[x].installed = false
|
app.config.packages[x].installed = false
|
||||||
}
|
}
|
||||||
|
|
||||||
for (var i = 0; i < app.metadata.database.packages.length; i++) {
|
for (let i = 0; i < app.metadata.database.packages.length; i++) {
|
||||||
// Find this config package
|
// Find this config package
|
||||||
for (var x = 0; x < app.config.packages.length; x++) {
|
for (let x = 0; x < app.config.packages.length; x++) {
|
||||||
if (app.config.packages[x].name === app.metadata.database.packages[i].name) {
|
if (app.config.packages[x].name === app.metadata.database.packages[i].name) {
|
||||||
app.config.packages[x].default = true
|
app.config.packages[x].default = true
|
||||||
app.config.packages[x].installed = true
|
app.config.packages[x].installed = true
|
||||||
|
@ -69,23 +72,27 @@ export default {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
this.$router.replace({ name: 'migrate',
|
this.$router.replace({
|
||||||
params: { next: app.metadata.is_launcher ? '/install/regular/false' : '/modify' } })
|
name: 'migrate',
|
||||||
|
params: { next: app.metadata.is_launcher ? '/install/regular/false' : '/modify' }
|
||||||
|
})
|
||||||
} else {
|
} else {
|
||||||
for (var x = 0; x < app.config.packages.length; x++) {
|
for (let x = 0; x < app.config.packages.length; x++) {
|
||||||
app.config.packages[x].installed = false
|
app.config.packages[x].installed = false
|
||||||
}
|
}
|
||||||
|
|
||||||
// Need to do a bit more digging to get at the
|
// Need to do a bit more digging to get at the
|
||||||
// install location.
|
// install location.
|
||||||
this.$root.ajax('/api/default-path', function (e) {
|
this.$http.get('/api/default-path').then(function (resp) {
|
||||||
if (e.path != null) {
|
if (resp.data.path != null) {
|
||||||
app.install_location = e.path
|
app.install_location = resp.data.path
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
this.$router.replace({ name: 'migrate',
|
this.$router.replace({
|
||||||
params: { next: '/packages' } })
|
name: 'migrate',
|
||||||
|
params: { next: '/packages' }
|
||||||
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,12 +1,12 @@
|
||||||
<template>
|
<template>
|
||||||
<div class="column" v-bind:class="{ 'has-padding': !$root.$data.metadata.is_launcher }">
|
<div class="column" v-bind:class="{ 'has-padding': !$root.$data.metadata.is_launcher }">
|
||||||
<b-message title="An error occurred" type="is-danger" :closable="false">
|
<b-message :title="$t('error.title')" type="is-danger" :closable="false">
|
||||||
<div id="error_msg" v-html="msg"></div>
|
<div id="error_msg" v-html="msg"></div>
|
||||||
</b-message>
|
</b-message>
|
||||||
<div class="field is-grouped is-right-floating is-bottom-floating">
|
<div class="field is-grouped is-right-floating" v-bind:class="{ 'is-bottom-floating': !$root.$data.metadata.is_launcher, 'is-top-floating': $root.$data.metadata.is_launcher }">
|
||||||
<p class="control">
|
<p class="control">
|
||||||
<a class="button is-primary is-medium" v-if="remaining && !$root.$data.metadata.is_launcher" v-on:click="go_back">Back</a>
|
<b-button class="is-primary is-medium" v-if="remaining && !$root.$data.metadata.is_launcher" v-on:click="go_back">{{ $t('back') }}</b-button>
|
||||||
<a class="button is-primary is-medium" v-if="$root.$data.metadata.is_launcher" v-on:click="exit">Exit</a>
|
<b-button class="is-primary is-medium" v-if="$root.$data.metadata.is_launcher" v-on:click="exit">{{ $t('exit') }}</b-button>
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -38,12 +38,12 @@ export default {
|
||||||
return {
|
return {
|
||||||
// https://stackoverflow.com/questions/6234773/can-i-escape-html-special-chars-in-javascript
|
// https://stackoverflow.com/questions/6234773/can-i-escape-html-special-chars-in-javascript
|
||||||
msg: this.$route.params.msg
|
msg: this.$route.params.msg
|
||||||
.replace(/&/g, "&")
|
.replace(/&/g, '&')
|
||||||
.replace(/</g, "<")
|
.replace(/</g, '<')
|
||||||
.replace(/>/g, ">")
|
.replace(/>/g, '>')
|
||||||
.replace(/"/g, """)
|
.replace(/"/g, '"')
|
||||||
.replace(/'/g, "'")
|
.replace(/'/g, ''')
|
||||||
.replace(/\n/g, "<br />"),
|
.replace(/\n/g, '<br />'),
|
||||||
remaining: window.history.length > 1
|
remaining: window.history.length > 1
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
|
@ -1,9 +1,9 @@
|
||||||
<template>
|
<template>
|
||||||
<div class="column has-padding">
|
<div class="column has-padding">
|
||||||
<h4 class="subtitle" v-if="$root.$data.metadata.is_launcher || is_update">Checking for updates...</h4>
|
<h4 class="subtitle" v-if="$root.$data.metadata.is_launcher || is_update">{{ $t('install_packages.check_for_update') }}</h4>
|
||||||
<h4 class="subtitle" v-else-if="is_uninstall">Uninstalling...</h4>
|
<h4 class="subtitle" v-else-if="is_uninstall">{{ $t('install_packages.uninstall') }}</h4>
|
||||||
<h4 class="subtitle" v-else-if="is_updater_update">Downloading self-update...</h4>
|
<h4 class="subtitle" v-else-if="is_updater_update">{{ $t('install_packages.self_update') }}</h4>
|
||||||
<h4 class="subtitle" v-else>Installing...</h4>
|
<h4 class="subtitle" v-else>{{ $t('install_packages.install') }}</h4>
|
||||||
<div v-html="$root.$data.config.installing_message"></div>
|
<div v-html="$root.$data.config.installing_message"></div>
|
||||||
<br />
|
<br />
|
||||||
|
|
||||||
|
@ -20,10 +20,11 @@ export default {
|
||||||
data: function () {
|
data: function () {
|
||||||
return {
|
return {
|
||||||
progress: 0.0,
|
progress: 0.0,
|
||||||
progress_message: 'Please wait...',
|
progress_message: this.$i18n.t('install_packages.please_wait'),
|
||||||
is_uninstall: false,
|
is_uninstall: false,
|
||||||
is_updater_update: false,
|
is_updater_update: false,
|
||||||
is_update: false,
|
is_update: false,
|
||||||
|
is_repair: false,
|
||||||
install_desktop_shortcut: false,
|
install_desktop_shortcut: false,
|
||||||
failed_with_error: false,
|
failed_with_error: false,
|
||||||
authorization_required: false,
|
authorization_required: false,
|
||||||
|
@ -35,30 +36,33 @@ export default {
|
||||||
this.is_updater_update = this.$route.params.kind === 'updater'
|
this.is_updater_update = this.$route.params.kind === 'updater'
|
||||||
this.is_update = this.$route.params.kind === 'update'
|
this.is_update = this.$route.params.kind === 'update'
|
||||||
this.install_desktop_shortcut = this.$route.params.desktop_shortcut === 'true'
|
this.install_desktop_shortcut = this.$route.params.desktop_shortcut === 'true'
|
||||||
|
this.is_repair = this.$route.params.kind === 'repair'
|
||||||
console.log('Installer kind: ' + this.$route.params.kind)
|
console.log('Installer kind: ' + this.$route.params.kind)
|
||||||
console.log('Installing desktop shortcut: ' + this.$route.params.desktop_shortcut)
|
console.log('Installing desktop shortcut: ' + this.$route.params.desktop_shortcut)
|
||||||
this.install()
|
this.install()
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
install: function () {
|
install: function () {
|
||||||
var that = this
|
const that = this
|
||||||
var app = this.$root
|
const app = this.$root
|
||||||
|
|
||||||
var results = {}
|
const results = {}
|
||||||
var requires_authorization = false;
|
|
||||||
|
|
||||||
for (var package_index = 0; package_index < app.config.packages.length; package_index++) {
|
for (let package_index = 0; package_index < app.config.packages.length; package_index++) {
|
||||||
var current_package = app.config.packages[package_index]
|
const current_package = app.config.packages[package_index]
|
||||||
if (current_package.default != null) {
|
if (current_package.default != null) {
|
||||||
requires_authorization |= current_package.requires_authorization;
|
|
||||||
results[current_package.name] = current_package.default
|
results[current_package.name] = current_package.default
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
results['path'] = app.install_location
|
results.path = app.install_location
|
||||||
results['installDesktopShortcut'] = that.install_desktop_shortcut
|
results.installDesktopShortcut = that.install_desktop_shortcut
|
||||||
|
|
||||||
var targetUrl = '/api/start-install'
|
if (this.is_repair) {
|
||||||
|
results.mode = 'force'
|
||||||
|
}
|
||||||
|
|
||||||
|
let targetUrl = '/api/start-install'
|
||||||
if (this.is_uninstall) {
|
if (this.is_uninstall) {
|
||||||
targetUrl = '/api/uninstall'
|
targetUrl = '/api/uninstall'
|
||||||
}
|
}
|
||||||
|
@ -69,20 +73,20 @@ export default {
|
||||||
this.$root.stream_ajax(targetUrl, function (line) {
|
this.$root.stream_ajax(targetUrl, function (line) {
|
||||||
// On progress line received from server
|
// On progress line received from server
|
||||||
|
|
||||||
if (line.hasOwnProperty('Status')) {
|
if (line.Status) {
|
||||||
that.progress_message = line.Status[0]
|
that.progress_message = line.Status[0]
|
||||||
that.progress = line.Status[1] * 100
|
that.progress = line.Status[1] * 100
|
||||||
}
|
}
|
||||||
|
|
||||||
if (line.hasOwnProperty('PackageInstalled')) {
|
if (line.PackageInstalled) {
|
||||||
that.packages_installed += 1
|
that.packages_installed += 1
|
||||||
}
|
}
|
||||||
|
|
||||||
if (line.hasOwnProperty('AuthorizationRequired')) {
|
if (line.AuthorizationRequired) {
|
||||||
that.authorization_required = true
|
that.authorization_required = true
|
||||||
}
|
}
|
||||||
|
|
||||||
if (line.hasOwnProperty('Error')) {
|
if (line.Error) {
|
||||||
that.failed_with_error = true
|
that.failed_with_error = true
|
||||||
that.$router.replace({ name: 'showerr', params: { msg: line.Error } })
|
that.$router.replace({ name: 'showerr', params: { msg: line.Error } })
|
||||||
}
|
}
|
||||||
|
@ -107,21 +111,23 @@ export default {
|
||||||
app.exit()
|
app.exit()
|
||||||
} else if (!that.failed_with_error) {
|
} else if (!that.failed_with_error) {
|
||||||
if (that.is_uninstall) {
|
if (that.is_uninstall) {
|
||||||
that.$router.replace({ name: 'complete',
|
that.$router.replace({
|
||||||
|
name: 'complete',
|
||||||
params: {
|
params: {
|
||||||
uninstall: true,
|
uninstall: true,
|
||||||
update: that.is_update,
|
update: that.is_update,
|
||||||
migrate: false,
|
|
||||||
installed: that.packages_installed
|
installed: that.packages_installed
|
||||||
} })
|
}
|
||||||
|
})
|
||||||
} else {
|
} else {
|
||||||
that.$router.replace({ name: 'complete',
|
that.$router.replace({
|
||||||
|
name: 'complete',
|
||||||
params: {
|
params: {
|
||||||
uninstall: false,
|
uninstall: false,
|
||||||
update: that.is_update,
|
update: that.is_update,
|
||||||
migrate: false,
|
|
||||||
installed: that.packages_installed
|
installed: that.packages_installed
|
||||||
} })
|
}
|
||||||
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,35 +1,34 @@
|
||||||
<template>
|
<template>
|
||||||
<div class="column has-padding">
|
<div class="column has-padding">
|
||||||
<h4 class="subtitle">Choose an option:</h4>
|
<h4 class="subtitle">{{ $t('modify.title') }}</h4>
|
||||||
|
|
||||||
<a class="button is-dark is-medium" v-on:click="update">
|
<b-button icon-left="update" type="is-dark-green" size="is-medium" @click="update">
|
||||||
Update
|
{{ $t('modify.update') }}
|
||||||
</a>
|
</b-button>
|
||||||
<br />
|
<br />
|
||||||
<br />
|
<br />
|
||||||
|
|
||||||
<a class="button is-dark is-medium" v-on:click="modify_packages">
|
<b-button icon-left="pencil" type="is-info" size="is-medium" @click="modify_packages">
|
||||||
Modify
|
{{ $t('modify.modify') }}
|
||||||
</a>
|
</b-button>
|
||||||
<br />
|
<br />
|
||||||
<br />
|
<br />
|
||||||
|
|
||||||
<a class="button is-dark is-medium" v-on:click="prepare_uninstall">
|
<b-button icon-left="wrench" type="is-info" size="is-medium" @click="repair_packages">
|
||||||
Uninstall
|
{{ $t('modify.repair') }}
|
||||||
</a>
|
</b-button>
|
||||||
|
<br />
|
||||||
|
<br />
|
||||||
|
|
||||||
<div class="modal is-active" v-if="show_uninstall">
|
<b-button icon-left="delete" type="is-danger" size="is-medium" @click="prepare_uninstall">
|
||||||
<div class="modal-background"></div>
|
{{ $t('modify.uninstall') }}
|
||||||
<div class="modal-card">
|
</b-button>
|
||||||
<header class="modal-card-head">
|
<br />
|
||||||
<p class="modal-card-title">Are you sure you want to uninstall {{ $root.$data.attrs.name }}?</p>
|
<br />
|
||||||
</header>
|
|
||||||
<footer class="modal-card-foot">
|
<b-button icon-left="file-find" type="is-link" size="is-medium" @click="view_files">
|
||||||
<button class="button is-danger" v-on:click="uninstall">Yes</button>
|
{{ $t('modify.view_local_files') }}
|
||||||
<button class="button" v-on:click="cancel_uninstall">No</button>
|
</b-button>
|
||||||
</footer>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
|
@ -37,9 +36,7 @@
|
||||||
export default {
|
export default {
|
||||||
name: 'ModifyView',
|
name: 'ModifyView',
|
||||||
data: function () {
|
data: function () {
|
||||||
return {
|
return {}
|
||||||
show_uninstall: false
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
update: function () {
|
update: function () {
|
||||||
|
@ -48,15 +45,42 @@ export default {
|
||||||
modify_packages: function () {
|
modify_packages: function () {
|
||||||
this.$router.push('/packages')
|
this.$router.push('/packages')
|
||||||
},
|
},
|
||||||
prepare_uninstall: function () {
|
repair_packages: function () {
|
||||||
this.show_uninstall = true
|
this.$router.push({ name: 'packages', params: { repair: true } })
|
||||||
},
|
},
|
||||||
cancel_uninstall: function () {
|
prepare_uninstall: function () {
|
||||||
this.show_uninstall = false
|
this.$buefy.dialog.confirm({
|
||||||
|
title: this.$t('modify.uninstall'),
|
||||||
|
message: this.$t('modify.prompt', { name: this.$root.$data.attrs.name }),
|
||||||
|
cancelText: this.$t('cancel'),
|
||||||
|
confirmText: this.$t('modify.prompt_confirm', { name: this.$root.$data.attrs.name }),
|
||||||
|
type: 'is-danger',
|
||||||
|
hasIcon: true,
|
||||||
|
onConfirm: this.uninstall
|
||||||
|
})
|
||||||
},
|
},
|
||||||
uninstall: function () {
|
uninstall: function () {
|
||||||
this.$router.push('/install/uninstall/false')
|
this.$router.push('/install/uninstall/false')
|
||||||
|
},
|
||||||
|
view_files: function () {
|
||||||
|
this.$http.get('/api/view-local-folder')
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
span {
|
||||||
|
cursor: unset !important;
|
||||||
|
}
|
||||||
|
.button.is-dark-green {
|
||||||
|
background-color: #00B245;
|
||||||
|
border-color: transparent;
|
||||||
|
color: #fff;
|
||||||
|
}
|
||||||
|
.button.is-dark-green:hover, .button.is-dark-green.is-hovered, .button.is-dark-green:focus {
|
||||||
|
background-color: #00a53f;
|
||||||
|
border-color: transparent;
|
||||||
|
color: #fff;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|
|
@ -1,158 +1,197 @@
|
||||||
<template>
|
<template>
|
||||||
<div class="column has-padding">
|
<div class="column has-padding">
|
||||||
<!-- Build options -->
|
<h4 class="subtitle" v-if="!repair">{{ $t('select_packages.title') }}</h4>
|
||||||
<div class="tile is-ancestor">
|
<h4 class="subtitle" v-if="repair">{{ $t('select_packages.title_repair') }}</h4>
|
||||||
<div class="tile is-parent is-vertical">
|
|
||||||
<div class="tile is-child is-12 box clickable-box" v-for="Lpackage in $root.$data.config.packages" :key="Lpackage.name" :index="Lpackage.name" v-on:click.capture.stop="clicked_box(Lpackage)">
|
<!-- Build options -->
|
||||||
<div class="ribbon" v-if="Lpackage.is_new"><span>New!</span></div>
|
<div class="tile is-ancestor">
|
||||||
<label class="checkbox">
|
<div class="tile is-parent" v-for="Lpackage in $root.$data.config.packages" :key="Lpackage.name" :index="Lpackage.name">
|
||||||
<b-checkbox v-model="Lpackage.default">
|
<div class="tile is-child">
|
||||||
<span v-if="!Lpackage.installed">Install</span> {{ Lpackage.name }}
|
<div class="box clickable-box" v-on:click.capture.stop="Lpackage.default = !Lpackage.default">
|
||||||
</b-checkbox>
|
<div class="ribbon" v-if="Lpackage.is_new"><span>New!</span></div>
|
||||||
<span v-if="Lpackage.installed"><i>(installed)</i></span>
|
<label class="checkbox">
|
||||||
</label>
|
<b-checkbox v-model="Lpackage.default">
|
||||||
<div>
|
{{ Lpackage.name }}
|
||||||
<img class="package-icon" :src="`${publicPath + Lpackage.icon}`"/>
|
</b-checkbox>
|
||||||
<p style="padding-top: 4px;" class="package-description">
|
<span v-if="Lpackage.installed"><i>{{ $t('select_packages.installed') }}</i></span>
|
||||||
{{ Lpackage.description }}
|
</label>
|
||||||
</p>
|
<div>
|
||||||
<p class="package-description">
|
<img class="package-icon" :src="`${publicPath + Lpackage.icon}`"/>
|
||||||
{{ get_extended_description(Lpackage) }}
|
<p style="padding-top: 4px;" class="package-description">
|
||||||
</p>
|
{{ Lpackage.description }}
|
||||||
</div>
|
</p>
|
||||||
|
<p class="package-description">
|
||||||
|
{{ get_extended_description(Lpackage) }}
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="tile is-child is-6 box clickable-box" v-if="!$root.$data.metadata.preexisting_install" v-on:click.capture.stop="installDesktopShortcut = !installDesktopShortcut">
|
||||||
|
<h4>Install Options</h4>
|
||||||
|
<b-checkbox v-model="installDesktopShortcut">
|
||||||
|
Create Desktop Shortcut
|
||||||
|
</b-checkbox>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="tile is-child is-6 box clickable-box" v-if="!$root.$data.metadata.preexisting_install" v-on:click.capture.stop="installDesktopShortcut = !installDesktopShortcut">
|
|
||||||
<h4>Install Options</h4>
|
<div class="subtitle is-6" v-if="!$root.$data.metadata.preexisting_install && advanced">{{ $t('select_packages.location') }}</div>
|
||||||
<b-checkbox v-model="installDesktopShortcut">
|
<div class="field has-addons" v-if="!$root.$data.metadata.preexisting_install && advanced">
|
||||||
Create Desktop Shortcut
|
<div class="control is-expanded">
|
||||||
</b-checkbox>
|
<input class="input" type="text" v-model="$root.$data.install_location"
|
||||||
|
:placeholder="$t('select_packages.location_placeholder')">
|
||||||
|
</div>
|
||||||
|
<div class="control">
|
||||||
|
<b-button class="is-dark" v-on:click="select_file">
|
||||||
|
{{ $t('select_packages.select') }}
|
||||||
|
</b-button>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="subtitle is-6" v-if="!$root.$data.metadata.preexisting_install && advanced">Install Location</div>
|
<div class="is-right-floating is-bottom-floating">
|
||||||
<div class="field has-addons" v-if="!$root.$data.metadata.preexisting_install && advanced">
|
<div class="field is-grouped">
|
||||||
<div class="control is-expanded">
|
<p class="control">
|
||||||
<input class="input" type="text" v-model="$root.$data.install_location"
|
<b-button class="is-medium" v-if="!$root.$data.config.hide_advanced && !$root.$data.metadata.preexisting_install && !advanced"
|
||||||
placeholder="Enter a install path here">
|
v-on:click="advanced = true">{{ $t('select_packages.advanced') }}</b-button>
|
||||||
</div>
|
</p>
|
||||||
<div class="control">
|
<p class="control">
|
||||||
<a class="button is-dark" v-on:click="select_file">
|
<b-button class="is-dark is-medium" v-if="!$root.$data.metadata.preexisting_install"
|
||||||
Select
|
v-on:click="install">{{ $t('select_packages.install') }}</b-button>
|
||||||
</a>
|
</p>
|
||||||
</div>
|
<p class="control">
|
||||||
</div>
|
<a class="button is-dark is-medium" v-if="$root.$data.metadata.preexisting_install"
|
||||||
|
v-on:click="install">{{ repair ? $t('select_packages.repair') : $t('select_packages.modify') }}</a>
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div class="is-right-floating is-bottom-floating">
|
<div class="field is-grouped is-left-floating is-bottom-floating">
|
||||||
<div class="field is-grouped">
|
<p class="control">
|
||||||
<p class="control">
|
<b-button class="is-medium" v-if="$root.$data.metadata.preexisting_install"
|
||||||
<a class="button is-medium" v-if="!$root.$data.config.hide_advanced && !$root.$data.metadata.preexisting_install && !advanced"
|
v-on:click="go_back">{{ $t('back') }}</b-button>
|
||||||
v-on:click="advanced = true">Advanced...</a>
|
</p>
|
||||||
</p>
|
</div>
|
||||||
<p class="control">
|
|
||||||
<!-- Disable the Install button on a fresh install with no packages selected -->
|
|
||||||
<button v-if="$root.$data.metadata.preexisting_install" class="button is-medium is-dark" v-on:click="install">
|
|
||||||
Modify
|
|
||||||
</button>
|
|
||||||
<button v-else class="button is-medium is-dark" v-on:click="install" :disabled="!this.has_package_selected">
|
|
||||||
Install
|
|
||||||
</button>
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="field is-grouped is-left-floating is-bottom-floating">
|
|
||||||
<p class="control">
|
|
||||||
<a class="button is-medium" v-if="$root.$data.metadata.preexisting_install"
|
|
||||||
v-on:click="go_back">Back</a>
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
export default {
|
export default {
|
||||||
name: 'SelectPackages',
|
name: 'SelectPackages',
|
||||||
created: function() {
|
data: function () {
|
||||||
// If they are authorized, make the packages that require authorization default
|
return {
|
||||||
// and also deselect any packages that don't use authorization
|
advanced: false,
|
||||||
if (this.$root.$data.has_reward_tier) {
|
repair: false,
|
||||||
for (let package_index = 0; package_index < this.$root.config.packages.length; package_index++) {
|
installDesktopShortcut: true
|
||||||
let current_package = this.$root.config.packages[package_index];
|
}
|
||||||
current_package.default = current_package.requires_authorization;
|
},
|
||||||
|
mounted: function () {
|
||||||
|
this.repair = this.$route.params.repair
|
||||||
|
// EA
|
||||||
|
// If they are authorized, make the packages that require authorization default
|
||||||
|
// and also deselect any packages that don't use authorization
|
||||||
|
if (this.$root.$data.has_reward_tier) {
|
||||||
|
for (let package_index = 0; package_index < this.$root.config.packages.length; package_index++) {
|
||||||
|
const current_package = this.$root.config.packages[package_index]
|
||||||
|
current_package.default = current_package.requires_authorization
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
select_file: function () {
|
||||||
|
window.external.invoke(JSON.stringify({
|
||||||
|
SelectInstallDir: {
|
||||||
|
callback_name: 'selectFileCallback'
|
||||||
}
|
}
|
||||||
|
}))
|
||||||
|
},
|
||||||
|
show_overwrite_dialog: function (confirmCallback) {
|
||||||
|
this.$buefy.dialog.confirm({
|
||||||
|
title: this.$t('select_packages.overwriting'),
|
||||||
|
message: this.$t('select_packages.overwriting_warning', { path: this.$root.$data.install_location }),
|
||||||
|
confirmText: this.$t('continue'),
|
||||||
|
cancelText: this.$t('cancel'),
|
||||||
|
type: 'is-danger',
|
||||||
|
hasIcon: true,
|
||||||
|
onConfirm: confirmCallback
|
||||||
|
})
|
||||||
|
},
|
||||||
|
show_nothing_picked_dialog: function () {
|
||||||
|
this.$buefy.dialog.alert({
|
||||||
|
title: this.$t('select_packages.nothing_picked'),
|
||||||
|
message: this.$t('select_packages.nothing_picked_warning', { path: this.$root.$data.install_location }),
|
||||||
|
confirmText: this.$t('cancel'),
|
||||||
|
type: 'is-danger',
|
||||||
|
hasIcon: true
|
||||||
|
})
|
||||||
|
},
|
||||||
|
install: function () {
|
||||||
|
if (!this.$root.config.packages.some(function (x) { return x.default })) {
|
||||||
|
this.show_nothing_picked_dialog()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
// maintenance + repair
|
||||||
|
if (this.repair) {
|
||||||
|
this.$router.push('/install/repair')
|
||||||
|
return
|
||||||
|
}
|
||||||
|
// maintenance + modify
|
||||||
|
if (this.$root.$data.metadata.preexisting_install) {
|
||||||
|
this.$router.push('/install/regular')
|
||||||
|
return
|
||||||
|
}
|
||||||
|
const my = this
|
||||||
|
this.$http.post('/api/verify-path', `path=${this.$root.$data.install_location}`).then(function (resp) {
|
||||||
|
const data = resp.data || {}
|
||||||
|
if (!data.exists) {
|
||||||
|
my.$router.push('/install/regular')
|
||||||
|
} else {
|
||||||
|
my.show_overwrite_dialog(function () {
|
||||||
|
my.$router.push('/install/repair')
|
||||||
|
})
|
||||||
|
}
|
||||||
|
})
|
||||||
|
},
|
||||||
|
go_back: function () {
|
||||||
|
this.$router.go(-1)
|
||||||
|
},
|
||||||
|
show_authentication: function () {
|
||||||
|
this.$router.push('/authentication')
|
||||||
|
},
|
||||||
|
show_authorization: function () {
|
||||||
|
this.$router.push('/authentication')
|
||||||
|
},
|
||||||
|
installable: function (pkg) {
|
||||||
|
return !pkg.requires_authorization || (pkg.requires_authorization && this.$root.$data.has_reward_tier)
|
||||||
|
},
|
||||||
|
clicked_box: function (pkg) {
|
||||||
|
if (this.installable(pkg)) {
|
||||||
|
pkg.default = !pkg.default
|
||||||
|
} else if (pkg.requires_authorization && !this.$root.$data.is_authenticated) {
|
||||||
|
this.show_authentication()
|
||||||
|
} else if (pkg.requires_authorization && !this.$root.$data.is_linked) {
|
||||||
|
this.show_authorization()
|
||||||
|
} else if (pkg.requires_authorization && !this.$root.$data.is_subscribed) {
|
||||||
|
this.show_authorization()
|
||||||
|
} else { // need_reward_tier_description
|
||||||
|
this.show_authorization()
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
data: function () {
|
get_extended_description: function (pkg) {
|
||||||
return {
|
if (!pkg.extended_description) {
|
||||||
publicPath: process.env.BASE_URL,
|
return ''
|
||||||
advanced: false,
|
|
||||||
installDesktopShortcut: true
|
|
||||||
}
|
}
|
||||||
},
|
if (this.installable(pkg)) {
|
||||||
computed: {
|
return pkg.extended_description.no_action_description
|
||||||
has_package_selected: function() {
|
} else if (pkg.requires_authorization && !this.$root.$data.is_authenticated) {
|
||||||
for (let i=0; i < this.$root.config.packages.length; ++i) {
|
return pkg.extended_description.need_authentication_description
|
||||||
let pkg = this.$root.config.packages[i];
|
} else if (pkg.requires_authorization && !this.$root.$data.is_linked) {
|
||||||
if (pkg.default) {
|
return pkg.extended_description.need_link_description
|
||||||
return true;
|
} else if (pkg.requires_authorization && !this.$root.$data.is_subscribed) {
|
||||||
}
|
return pkg.extended_description.need_subscription_description
|
||||||
}
|
} else { // need_reward_tier_description
|
||||||
return false;
|
return pkg.extended_description.need_reward_tier_description
|
||||||
}
|
|
||||||
},
|
|
||||||
methods: {
|
|
||||||
select_file: function () {
|
|
||||||
window.external.invoke(JSON.stringify({
|
|
||||||
SelectInstallDir: {
|
|
||||||
callback_name: 'selectFileCallback'
|
|
||||||
}
|
|
||||||
}))
|
|
||||||
},
|
|
||||||
install: function () {
|
|
||||||
this.$router.push('/install/regular/' + this.installDesktopShortcut.toString())
|
|
||||||
},
|
|
||||||
go_back: function () {
|
|
||||||
this.$router.go(-1)
|
|
||||||
},
|
|
||||||
show_authentication: function () {
|
|
||||||
this.$router.push('/authentication')
|
|
||||||
},
|
|
||||||
show_authorization: function () {
|
|
||||||
this.$router.push('/authentication')
|
|
||||||
},
|
|
||||||
installable: function (pkg) {
|
|
||||||
return !pkg.requires_authorization || (pkg.requires_authorization && this.$root.$data.has_reward_tier);
|
|
||||||
},
|
|
||||||
clicked_box: function (pkg) {
|
|
||||||
if (this.installable(pkg)) {
|
|
||||||
pkg.default = !pkg.default;
|
|
||||||
} else if (pkg.requires_authorization && !this.$root.$data.is_authenticated) {
|
|
||||||
this.show_authentication()
|
|
||||||
} else if (pkg.requires_authorization && !this.$root.$data.is_linked) {
|
|
||||||
this.show_authorization()
|
|
||||||
} else if (pkg.requires_authorization && !this.$root.$data.is_subscribed) {
|
|
||||||
this.show_authorization()
|
|
||||||
} else { // need_reward_tier_description
|
|
||||||
this.show_authorization()
|
|
||||||
}
|
|
||||||
},
|
|
||||||
get_extended_description: function(pkg) {
|
|
||||||
if (!pkg.extended_description) {
|
|
||||||
return "";
|
|
||||||
}
|
|
||||||
if (this.installable(pkg)) {
|
|
||||||
return pkg.extended_description.no_action_description;
|
|
||||||
} else if (pkg.requires_authorization && !this.$root.$data.is_authenticated) {
|
|
||||||
return pkg.extended_description.need_authentication_description;
|
|
||||||
} else if (pkg.requires_authorization && !this.$root.$data.is_linked) {
|
|
||||||
return pkg.extended_description.need_link_description;
|
|
||||||
} else if (pkg.requires_authorization && !this.$root.$data.is_subscribed) {
|
|
||||||
return pkg.extended_description.need_subscription_description;
|
|
||||||
} else { // need_reward_tier_description
|
|
||||||
return pkg.extended_description.need_reward_tier_description;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
7789
ui/yarn.lock
7789
ui/yarn.lock
File diff suppressed because it is too large
Load diff
Loading…
Reference in a new issue