mirror of
https://github.com/yuzu-emu/liftinstall.git
synced 2024-12-22 20:15:44 +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]
|
||||
name = "liftinstall"
|
||||
version = "0.1.0"
|
||||
version = "0.2.0"
|
||||
edition = "2018"
|
||||
authors = ["James <jselby@jselby.net>"]
|
||||
repository = "https://github.com/j-selby/liftinstall.git"
|
||||
documentation = "https://liftinstall.jselby.net"
|
||||
|
@ -8,35 +9,36 @@ description = "An adaptable installer for your application."
|
|||
build = "build.rs"
|
||||
|
||||
[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"
|
||||
futures = "0.1.25"
|
||||
mime_guess = "1.8.6"
|
||||
url = "1.7.2"
|
||||
futures = "0.1.29"
|
||||
mime_guess = "2.0"
|
||||
url = "2.2"
|
||||
|
||||
reqwest = "0.9.21"
|
||||
number_prefix = "0.3.0"
|
||||
reqwest = "0.9.22"
|
||||
number_prefix = "0.4"
|
||||
|
||||
serde = "1.0.89"
|
||||
serde_derive = "1.0.89"
|
||||
serde_json = "1.0.39"
|
||||
serde = "1.0"
|
||||
serde_derive = "1.0"
|
||||
serde_json = "1.0"
|
||||
|
||||
toml = "0.5.0"
|
||||
toml = "0.5"
|
||||
|
||||
semver = {version = "0.9.0", features = ["serde"]}
|
||||
regex = "1.1.5"
|
||||
semver = {version = "1.0", features = ["serde"]}
|
||||
regex = "1.4"
|
||||
|
||||
dirs = "1.0.5"
|
||||
zip = "0.5.1"
|
||||
xz2 = "0.1.6"
|
||||
dirs = "3.0"
|
||||
zip = "0.5"
|
||||
xz2 = "0.1"
|
||||
tar = "0.4"
|
||||
|
||||
log = "0.4"
|
||||
fern = "0.5"
|
||||
chrono = "0.4.6"
|
||||
fern = "0.6"
|
||||
chrono = "0.4"
|
||||
|
||||
clap = "2.32.0"
|
||||
clap = "2.33"
|
||||
|
||||
# used to open a link to the users default browser
|
||||
webbrowser = "0.5.2"
|
||||
|
@ -46,19 +48,19 @@ jsonwebtoken = "6"
|
|||
base64 = "0.10.1"
|
||||
|
||||
[build-dependencies]
|
||||
walkdir = "2.2.7"
|
||||
serde = "1.0.89"
|
||||
serde_derive = "1.0.89"
|
||||
toml = "0.5.0"
|
||||
which = "2.0.1"
|
||||
walkdir = "2.3"
|
||||
serde = "1.0"
|
||||
serde_derive = "1.0"
|
||||
toml = "0.5"
|
||||
which = "4.0"
|
||||
|
||||
[target.'cfg(windows)'.dependencies]
|
||||
winapi = { version = "0.3", features = ["psapi", "winbase", "winioctl", "winnt"] }
|
||||
widestring = "0.4.0"
|
||||
widestring = "0.4"
|
||||
|
||||
[target.'cfg(not(windows))'.dependencies]
|
||||
sysinfo = "0.8.2"
|
||||
slug = "0.1.4"
|
||||
sysinfo = "0.18"
|
||||
slug = "0.1"
|
||||
|
||||
[target.'cfg(windows)'.build-dependencies]
|
||||
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()
|
||||
.cpp(true)
|
||||
.define("_WIN32_WINNT", Some("0x0600"))
|
||||
.define("WINVER", Some("0x0600"))
|
||||
.file("src/native/interop.cpp")
|
||||
.compile("interop");
|
||||
}
|
||||
|
@ -102,7 +104,7 @@ fn main() {
|
|||
.unwrap()
|
||||
.wait()
|
||||
.expect("Unable to install Node.JS dependencies using Yarn");
|
||||
Command::new(&yarn_binary)
|
||||
let return_code = Command::new(&yarn_binary)
|
||||
.args(&[
|
||||
"--cwd",
|
||||
ui_dir.to_str().expect("Unable to covert path"),
|
||||
|
@ -114,8 +116,7 @@ fn main() {
|
|||
.to_str()
|
||||
.expect("Unable to convert path"),
|
||||
])
|
||||
.spawn()
|
||||
.unwrap()
|
||||
.wait()
|
||||
.status()
|
||||
.expect("Unable to build frontend assets using Webpack");
|
||||
assert!(return_code.success());
|
||||
}
|
||||
|
|
|
@ -41,7 +41,7 @@ impl<'a> Archive<'a> for ZipArchive<'a> {
|
|||
continue;
|
||||
}
|
||||
|
||||
func(i, Some(max), archive.sanitized_name(), &mut archive)?;
|
||||
func(i, Some(max), archive.mangled_name(), &mut archive)?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
|
|
|
@ -7,8 +7,8 @@ use toml::de::Error as TomlError;
|
|||
|
||||
use serde_json::{self, Error as SerdeError};
|
||||
|
||||
use sources::get_by_name;
|
||||
use sources::types::Release;
|
||||
use crate::sources::get_by_name;
|
||||
use crate::sources::types::Release;
|
||||
|
||||
/// Description of the source of a package.
|
||||
#[derive(Debug, Deserialize, Serialize, Clone)]
|
||||
|
|
|
@ -4,8 +4,8 @@
|
|||
|
||||
use std::sync::{Arc, RwLock};
|
||||
|
||||
use installer::InstallerFramework;
|
||||
use logging::LoggingErrors;
|
||||
use crate::installer::InstallerFramework;
|
||||
use crate::logging::LoggingErrors;
|
||||
|
||||
pub mod rest;
|
||||
mod ui;
|
||||
|
|
|
@ -2,7 +2,8 @@
|
|||
|
||||
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 {
|
||||
( $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) => {
|
||||
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();
|
||||
|
@ -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.woff",
|
||||
"/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.map",
|
||||
"/js/app.js",
|
||||
"/js/app.js.map"
|
||||
"/js/app.js"
|
||||
)?;
|
||||
|
||||
Some((string_mime, contents))
|
||||
|
|
|
@ -2,11 +2,11 @@
|
|||
//!
|
||||
//! 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;
|
||||
|
||||
|
|
|
@ -2,27 +2,23 @@
|
|||
//!
|
||||
//! The /api/attr call returns an executable script containing session variables.
|
||||
|
||||
use frontend::rest::services::default_future;
|
||||
use frontend::rest::services::encapsulate_json;
|
||||
use frontend::rest::services::Future;
|
||||
use frontend::rest::services::Request;
|
||||
use frontend::rest::services::Response;
|
||||
use frontend::rest::services::WebService;
|
||||
use crate::frontend::rest::services::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 logging::LoggingErrors;
|
||||
use crate::logging::LoggingErrors;
|
||||
|
||||
pub fn handle(service: &WebService, _req: Request) -> Future {
|
||||
let framework = service.get_framework_read();
|
||||
|
||||
let file = encapsulate_json(
|
||||
"base_attributes",
|
||||
&framework
|
||||
.base_attributes
|
||||
.to_json_str()
|
||||
.log_expect("Failed to render JSON representation of config"),
|
||||
);
|
||||
let file = framework
|
||||
.base_attributes
|
||||
.to_json_str()
|
||||
.log_expect("Failed to render JSON representation of config");
|
||||
|
||||
default_future(
|
||||
Response::new()
|
||||
|
|
|
@ -4,18 +4,18 @@
|
|||
//!
|
||||
//! This endpoint should be usable directly from a <script> tag during loading.
|
||||
|
||||
use frontend::rest::services::Future;
|
||||
use frontend::rest::services::Request;
|
||||
use frontend::rest::services::Response;
|
||||
use frontend::rest::services::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::{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::Future as _;
|
||||
|
|
|
@ -2,17 +2,17 @@
|
|||
//!
|
||||
//! This call returns if dark mode is enabled on the system currently.
|
||||
|
||||
use frontend::rest::services::default_future;
|
||||
use frontend::rest::services::Future;
|
||||
use frontend::rest::services::Request;
|
||||
use frontend::rest::services::Response;
|
||||
use frontend::rest::services::WebService;
|
||||
use crate::frontend::rest::services::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 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 {
|
||||
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.
|
||||
|
||||
use frontend::rest::services::default_future;
|
||||
use frontend::rest::services::Future;
|
||||
use frontend::rest::services::Request;
|
||||
use frontend::rest::services::Response;
|
||||
use frontend::rest::services::WebService;
|
||||
use crate::frontend::rest::services::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 logging::LoggingErrors;
|
||||
use crate::logging::LoggingErrors;
|
||||
|
||||
/// Struct used by serde to send a JSON payload to the client containing an optional value.
|
||||
#[derive(Serialize)]
|
||||
|
|
|
@ -2,15 +2,18 @@
|
|||
//!
|
||||
//! The /api/exit closes down the application.
|
||||
|
||||
use frontend::rest::services::Future as InternalFuture;
|
||||
use frontend::rest::services::{default_future, Request, Response, WebService};
|
||||
use crate::frontend::rest::services::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::ContentType;
|
||||
use hyper::StatusCode;
|
||||
|
||||
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() {
|
||||
Ok(_) => {
|
||||
exit(0);
|
||||
|
|
|
@ -2,14 +2,14 @@
|
|||
//!
|
||||
//! The /api/install call installs a set of packages dictated by a POST request.
|
||||
|
||||
use frontend::rest::services::stream_progress;
|
||||
use frontend::rest::services::Future;
|
||||
use frontend::rest::services::Request;
|
||||
use frontend::rest::services::WebService;
|
||||
use crate::frontend::rest::services::stream_progress;
|
||||
use crate::frontend::rest::services::Future;
|
||||
use crate::frontend::rest::services::Request;
|
||||
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::stream::Stream;
|
||||
|
@ -23,11 +23,12 @@ 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>>();
|
||||
|
||||
.into_owned()
|
||||
.collect::<HashMap<String, String>>();
|
||||
|
||||
let mut to_install = Vec::new();
|
||||
let mut path: Option<String> = None;
|
||||
let mut force_install = false;
|
||||
let mut install_desktop_shortcut= false;
|
||||
|
||||
// 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" {
|
||||
info!("Found installDesktopShortcut {:?}", value);
|
||||
install_desktop_shortcut = value == "true";
|
||||
}
|
||||
|
||||
if key == "mode" && value == "force" {
|
||||
force_install = true;
|
||||
continue;
|
||||
}
|
||||
|
||||
|
@ -60,7 +65,7 @@ pub fn handle(service: &WebService, req: Request) -> Future {
|
|||
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);
|
||||
if let Err(v) = sender.send(InstallMessage::Error(v)) {
|
||||
error!("Failed to send install error: {:?}", v);
|
||||
|
|
|
@ -5,15 +5,15 @@
|
|||
//!
|
||||
//! e.g. if the application is in maintenance mode
|
||||
|
||||
use frontend::rest::services::default_future;
|
||||
use frontend::rest::services::Future;
|
||||
use frontend::rest::services::Request;
|
||||
use frontend::rest::services::Response;
|
||||
use frontend::rest::services::WebService;
|
||||
use crate::frontend::rest::services::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 logging::LoggingErrors;
|
||||
use crate::logging::LoggingErrors;
|
||||
|
||||
pub fn handle(service: &WebService, _req: Request) -> Future {
|
||||
let framework = service.get_framework_read();
|
||||
|
|
|
@ -4,12 +4,12 @@
|
|||
|
||||
use std::sync::{Arc, RwLock, RwLockReadGuard, RwLockWriteGuard};
|
||||
|
||||
use installer::{InstallMessage, InstallerFramework};
|
||||
use crate::installer::{InstallMessage, InstallerFramework};
|
||||
|
||||
use hyper::server::Service;
|
||||
use hyper::{Method, StatusCode};
|
||||
|
||||
use logging::LoggingErrors;
|
||||
use crate::logging::LoggingErrors;
|
||||
|
||||
use std::sync::mpsc::{channel, Sender};
|
||||
|
||||
|
@ -33,6 +33,8 @@ mod packages;
|
|||
mod static_files;
|
||||
mod uninstall;
|
||||
mod update_updater;
|
||||
mod verify_path;
|
||||
mod view_folder;
|
||||
|
||||
/// Expected incoming Request format from Hyper.
|
||||
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/dark-mode") => dark_mode::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/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/start-install") => install::handle(self, req),
|
||||
(Method::Post, "/api/open-browser") => browser::handle(self, req),
|
||||
(Method::Post, "/api/uninstall") => uninstall::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),
|
||||
e => {
|
||||
info!("Returned 404 for {:?}", e);
|
||||
|
|
|
@ -2,16 +2,16 @@
|
|||
//!
|
||||
//! The /api/packages call returns all the currently installed packages.
|
||||
|
||||
use frontend::rest::services::default_future;
|
||||
use frontend::rest::services::encapsulate_json;
|
||||
use frontend::rest::services::Future;
|
||||
use frontend::rest::services::Request;
|
||||
use frontend::rest::services::Response;
|
||||
use frontend::rest::services::WebService;
|
||||
use crate::frontend::rest::services::default_future;
|
||||
use crate::frontend::rest::services::encapsulate_json;
|
||||
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 logging::LoggingErrors;
|
||||
use crate::logging::LoggingErrors;
|
||||
|
||||
pub fn handle(service: &WebService, _req: Request) -> Future {
|
||||
let framework = service.get_framework_read();
|
||||
|
|
|
@ -4,18 +4,18 @@
|
|||
//!
|
||||
//! e.g. index.html, main.js, ...
|
||||
|
||||
use frontend::rest::assets;
|
||||
use crate::frontend::rest::assets;
|
||||
|
||||
use frontend::rest::services::default_future;
|
||||
use frontend::rest::services::Future;
|
||||
use frontend::rest::services::Request;
|
||||
use frontend::rest::services::Response;
|
||||
use frontend::rest::services::WebService;
|
||||
use crate::frontend::rest::services::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 hyper::StatusCode;
|
||||
|
||||
use logging::LoggingErrors;
|
||||
use crate::logging::LoggingErrors;
|
||||
|
||||
pub fn handle(_service: &WebService, req: Request) -> Future {
|
||||
// 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.
|
||||
|
||||
use frontend::rest::services::default_future;
|
||||
use frontend::rest::services::stream_progress;
|
||||
use frontend::rest::services::Future;
|
||||
use frontend::rest::services::Request;
|
||||
use frontend::rest::services::WebService;
|
||||
use crate::frontend::rest::services::default_future;
|
||||
use crate::frontend::rest::services::stream_progress;
|
||||
use crate::frontend::rest::services::Future;
|
||||
use crate::frontend::rest::services::Request;
|
||||
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 {
|
||||
let framework = service.framework.clone();
|
||||
|
|
|
@ -2,15 +2,15 @@
|
|||
//!
|
||||
//! The /api/update-updater call attempts to update the currently running updater.
|
||||
|
||||
use frontend::rest::services::default_future;
|
||||
use frontend::rest::services::stream_progress;
|
||||
use frontend::rest::services::Future;
|
||||
use frontend::rest::services::Request;
|
||||
use frontend::rest::services::WebService;
|
||||
use crate::frontend::rest::services::default_future;
|
||||
use crate::frontend::rest::services::stream_progress;
|
||||
use crate::frontend::rest::services::Future;
|
||||
use crate::frontend::rest::services::Request;
|
||||
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 {
|
||||
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 logging::LoggingErrors;
|
||||
use crate::logging::LoggingErrors;
|
||||
|
||||
use log::Level;
|
||||
|
||||
|
@ -16,8 +16,8 @@ enum CallbackType {
|
|||
}
|
||||
|
||||
/// Starts the main web UI. Will return when UI is closed.
|
||||
pub fn start_ui(app_name: &str, http_address: &str, _is_launcher: bool) {
|
||||
let size = (1024, 550);
|
||||
pub fn start_ui(app_name: &str, http_address: &str, is_launcher: bool) {
|
||||
let size = if is_launcher { (600, 300) } else { (1024, 500) };
|
||||
|
||||
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))
|
||||
.size(size.0, size.1)
|
||||
.resizable(false)
|
||||
.debug(false)
|
||||
.debug(cfg!(debug_assertions))
|
||||
.user_data(())
|
||||
.invoke_handler(|wv, msg| {
|
||||
let mut cb_result = Ok(());
|
||||
|
@ -37,15 +37,14 @@ pub fn start_ui(app_name: &str, http_address: &str, _is_launcher: bool) {
|
|||
|
||||
match command {
|
||||
CallbackType::SelectInstallDir { callback_name } => {
|
||||
let result = wv
|
||||
.dialog()
|
||||
.choose_directory("Select a install directory...", "");
|
||||
let result =
|
||||
tinyfiledialogs::select_folder_dialog("Select a install directory...", "");
|
||||
|
||||
if let Ok(Some(new_path)) = result {
|
||||
if new_path.to_string_lossy().len() > 0 {
|
||||
if let Some(new_path) = result {
|
||||
if !new_path.is_empty() {
|
||||
let result = serde_json::to_string(&new_path)
|
||||
.log_expect("Unable to serialize response");
|
||||
let command = format!("{}({});", callback_name, result);
|
||||
let command = format!("window.{}({});", callback_name, result);
|
||||
debug!("Injecting response: {}", command);
|
||||
cb_result = wv.eval(&command);
|
||||
}
|
||||
|
|
|
@ -7,7 +7,7 @@ use reqwest::header::CONTENT_LENGTH;
|
|||
use std::io::Read;
|
||||
use std::time::Duration;
|
||||
|
||||
use reqwest::async::Client as AsyncClient;
|
||||
use reqwest::r#async::Client as AsyncClient;
|
||||
use reqwest::Client;
|
||||
use reqwest::StatusCode;
|
||||
|
||||
|
|
|
@ -21,28 +21,28 @@ use std::io::Cursor;
|
|||
use std::process::Command;
|
||||
use std::process::{exit, Stdio};
|
||||
|
||||
use config::BaseAttributes;
|
||||
use config::Config;
|
||||
use crate::config::BaseAttributes;
|
||||
use crate::config::Config;
|
||||
|
||||
use sources::types::Version;
|
||||
use crate::sources::types::Version;
|
||||
|
||||
use tasks::install::InstallTask;
|
||||
use tasks::uninstall::UninstallTask;
|
||||
use tasks::uninstall_global_shortcut::UninstallGlobalShortcutsTask;
|
||||
use tasks::DependencyTree;
|
||||
use tasks::TaskMessage;
|
||||
use crate::tasks::install::InstallTask;
|
||||
use crate::tasks::uninstall::UninstallTask;
|
||||
use crate::tasks::uninstall_global_shortcut::UninstallGlobalShortcutsTask;
|
||||
use crate::tasks::DependencyTree;
|
||||
use crate::tasks::TaskMessage;
|
||||
|
||||
use logging::LoggingErrors;
|
||||
use crate::logging::LoggingErrors;
|
||||
|
||||
use dirs::home_dir;
|
||||
|
||||
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.
|
||||
#[derive(Serialize)]
|
||||
|
@ -174,12 +174,14 @@ impl InstallerFramework {
|
|||
/// items: Array of named packages to be installed/kept
|
||||
/// messages: Channel used to send progress messages
|
||||
/// fresh_install: If the install directory must be empty
|
||||
/// force_install: If the install directory should be erased first
|
||||
pub fn install(
|
||||
&mut self,
|
||||
items: Vec<String>,
|
||||
messages: &Sender<InstallMessage>,
|
||||
fresh_install: bool,
|
||||
create_desktop_shortcuts: bool,
|
||||
force_install: bool,
|
||||
) -> Result<(), String> {
|
||||
info!(
|
||||
"Framework: Installing {:?} to {:?}",
|
||||
|
@ -209,6 +211,7 @@ impl InstallerFramework {
|
|||
uninstall_items,
|
||||
fresh_install,
|
||||
create_desktop_shortcuts,
|
||||
force_install
|
||||
});
|
||||
|
||||
let mut tree = DependencyTree::build(task);
|
||||
|
|
|
@ -74,7 +74,7 @@ use config::BaseAttributes;
|
|||
use std::process::{Command, Stdio, exit};
|
||||
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() {
|
||||
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);
|
||||
|
||||
// Handle self-updating if needed
|
||||
let current_exe = std::env::current_exe().log_expect("Current executable could not be found");
|
||||
let current_path = current_exe
|
||||
.parent()
|
||||
|
|
|
@ -46,7 +46,7 @@ extern "C" int saveShortcut(
|
|||
const wchar_t *workingDir,
|
||||
const wchar_t *exePath)
|
||||
{
|
||||
const char *errStr = NULL;
|
||||
char *errStr = NULL;
|
||||
HRESULT h;
|
||||
IShellLink *shellLink = NULL;
|
||||
IPersistFile *persistFile = NULL;
|
||||
|
@ -168,15 +168,3 @@ extern "C" HRESULT getSystemFolder(wchar_t *out_path)
|
|||
}
|
||||
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;
|
||||
|
||||
use logging::LoggingErrors;
|
||||
use crate::logging::LoggingErrors;
|
||||
|
||||
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};
|
||||
|
||||
|
@ -26,11 +29,13 @@ mod natives {
|
|||
use winapi::um::psapi::{
|
||||
EnumProcessModulesEx, GetModuleFileNameExW, K32EnumProcesses, LIST_MODULES_ALL,
|
||||
};
|
||||
use winapi::um::shellapi::ShellExecuteW;
|
||||
use winapi::um::winnt::{
|
||||
HANDLE, PROCESS_QUERY_INFORMATION, PROCESS_TERMINATE, PROCESS_VM_READ,
|
||||
};
|
||||
use winapi::um::winuser::SW_SHOWDEFAULT;
|
||||
|
||||
use widestring::{U16CString};
|
||||
use widestring::U16CString;
|
||||
|
||||
extern "C" {
|
||||
pub fn saveShortcut(
|
||||
|
@ -50,28 +55,6 @@ mod natives {
|
|||
) -> ::std::os::raw::c_int;
|
||||
|
||||
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
|
||||
|
@ -139,15 +122,23 @@ mod natives {
|
|||
}
|
||||
}
|
||||
|
||||
fn count_u16(u16str: &[u16]) -> usize {
|
||||
let mut pos = 0;
|
||||
for x in u16str.iter() {
|
||||
if *x == 0 {
|
||||
break;
|
||||
}
|
||||
pos += 1;
|
||||
// Needed to call unsafe function `ShellExecuteW` from `winapi` crate
|
||||
#[allow(unsafe_code)]
|
||||
pub fn open_in_shell(path: &Path) {
|
||||
let native_verb = U16CString::from_str("open").unwrap();
|
||||
// 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();
|
||||
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
|
||||
|
@ -179,13 +170,20 @@ mod natives {
|
|||
let spawn_result: i32 = unsafe {
|
||||
let mut cmd_path = [0u16; MAX_PATH + 1];
|
||||
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 {
|
||||
return;
|
||||
}
|
||||
|
||||
spawnDetached(
|
||||
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")
|
||||
.as_ptr(),
|
||||
|
@ -297,7 +295,7 @@ mod natives {
|
|||
|
||||
use std::env;
|
||||
|
||||
use logging::LoggingErrors;
|
||||
use crate::logging::LoggingErrors;
|
||||
|
||||
use sysinfo::{ProcessExt, SystemExt};
|
||||
|
||||
|
@ -306,6 +304,8 @@ mod natives {
|
|||
use slug::slugify;
|
||||
use std::fs::{create_dir_all, File};
|
||||
use std::io::Write;
|
||||
use std::path::Path;
|
||||
use std::process::Command;
|
||||
|
||||
#[cfg(target_os = "linux")]
|
||||
pub fn create_shortcut(
|
||||
|
@ -314,6 +314,7 @@ mod natives {
|
|||
target: &str,
|
||||
args: &str,
|
||||
working_dir: &str,
|
||||
_exe_path: &str,
|
||||
) -> Result<String, String> {
|
||||
// FIXME: no icon will be shown since no icon is provided
|
||||
let data_local_dir = dirs::data_local_dir();
|
||||
|
@ -322,7 +323,7 @@ mod natives {
|
|||
let mut path = x;
|
||||
path.push("applications");
|
||||
match create_dir_all(path.to_path_buf()) {
|
||||
Ok(_) => (()),
|
||||
Ok(_) => (),
|
||||
Err(e) => {
|
||||
return Err(format!(
|
||||
"Local data directory does not exist and cannot be created: {}",
|
||||
|
@ -340,7 +341,7 @@ mod natives {
|
|||
Ok(file) => file,
|
||||
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 {
|
||||
Ok(_) => Ok("".to_string()),
|
||||
Err(e) => Err(format!("Unable to write desktop file: {}", e)),
|
||||
|
@ -358,11 +359,25 @@ mod natives {
|
|||
target: &str,
|
||||
args: &str,
|
||||
working_dir: &str,
|
||||
_exe_path: &str,
|
||||
) -> Result<String, String> {
|
||||
warn!("STUB! Creating shortcut is not implemented on macOS");
|
||||
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
|
||||
pub fn burn_on_exit(app_name: &str) {
|
||||
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 system = sysinfo::System::new();
|
||||
system.refresh_all();
|
||||
for (pid, procs) in system.get_process_list() {
|
||||
for (pid, procs) in system.get_processes() {
|
||||
processes.push(super::Process {
|
||||
pid: *pid as usize,
|
||||
name: procs.name().to_string(),
|
||||
|
|
|
@ -9,7 +9,7 @@ use std::{thread, time};
|
|||
|
||||
use clap::{App, ArgMatches};
|
||||
|
||||
use logging::LoggingErrors;
|
||||
use crate::logging::LoggingErrors;
|
||||
|
||||
/// Swaps around the main executable if needed.
|
||||
pub fn perform_swap(current_exe: &PathBuf, to_path: Option<&str>) {
|
||||
|
|
|
@ -7,9 +7,9 @@ use reqwest::StatusCode;
|
|||
|
||||
use serde_json;
|
||||
|
||||
use sources::types::*;
|
||||
use crate::sources::types::*;
|
||||
|
||||
use http::build_client;
|
||||
use crate::http::build_client;
|
||||
|
||||
pub struct GithubReleases {}
|
||||
|
||||
|
|
|
@ -24,7 +24,7 @@ impl Version {
|
|||
match *self {
|
||||
Version::Semver(ref version) => version.to_owned(),
|
||||
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.
|
||||
|
||||
use installer::InstallerFramework;
|
||||
use crate::installer::InstallerFramework;
|
||||
|
||||
use tasks::check_authorization::CheckAuthorizationTask;
|
||||
use tasks::{Task, TaskDependency, TaskMessage, TaskOrdering, TaskParamType};
|
||||
use crate::tasks::check_authorization::CheckAuthorizationTask;
|
||||
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 name: String,
|
||||
|
|
|
@ -1,14 +1,14 @@
|
|||
//! 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 tasks::TaskDependency;
|
||||
use tasks::TaskMessage;
|
||||
use tasks::TaskParamType;
|
||||
use crate::tasks::Task;
|
||||
use crate::tasks::TaskDependency;
|
||||
use crate::tasks::TaskMessage;
|
||||
use crate::tasks::TaskParamType;
|
||||
|
||||
use native::get_process_names;
|
||||
use native::Process;
|
||||
use crate::native::get_process_names;
|
||||
use crate::native::Process;
|
||||
|
||||
use std::process;
|
||||
|
||||
|
|
|
@ -1,26 +1,29 @@
|
|||
//! Overall hierarchy for installing a installation of the application.
|
||||
|
||||
use installer::InstallerFramework;
|
||||
use crate::installer::InstallerFramework;
|
||||
|
||||
use tasks::ensure_only_instance::EnsureOnlyInstanceTask;
|
||||
use tasks::install_dir::VerifyInstallDirTask;
|
||||
use tasks::install_global_shortcut::InstallGlobalShortcutsTask;
|
||||
use tasks::install_pkg::InstallPackageTask;
|
||||
use tasks::save_executable::SaveExecutableTask;
|
||||
use tasks::uninstall_pkg::UninstallPackageTask;
|
||||
use tasks::launch_installed_on_exit::LaunchOnExitTask;
|
||||
use crate::tasks::ensure_only_instance::EnsureOnlyInstanceTask;
|
||||
use crate::tasks::install_dir::VerifyInstallDirTask;
|
||||
use crate::tasks::install_global_shortcut::InstallGlobalShortcutsTask;
|
||||
use crate::tasks::install_pkg::InstallPackageTask;
|
||||
use crate::tasks::remove_target_dir::RemoveTargetDirTask;
|
||||
use crate::tasks::save_executable::SaveExecutableTask;
|
||||
use crate::tasks::uninstall_pkg::UninstallPackageTask;
|
||||
use crate::tasks::launch_installed_on_exit::LaunchOnExitTask;
|
||||
|
||||
use tasks::Task;
|
||||
use tasks::TaskDependency;
|
||||
use tasks::TaskMessage;
|
||||
use tasks::TaskOrdering;
|
||||
use tasks::TaskParamType;
|
||||
use crate::tasks::Task;
|
||||
use crate::tasks::TaskDependency;
|
||||
use crate::tasks::TaskMessage;
|
||||
use crate::tasks::TaskOrdering;
|
||||
use crate::tasks::TaskParamType;
|
||||
|
||||
pub struct InstallTask {
|
||||
pub items: Vec<String>,
|
||||
pub uninstall_items: Vec<String>,
|
||||
pub fresh_install: bool,
|
||||
pub create_desktop_shortcuts: bool,
|
||||
// force_install: remove the target directory before installing
|
||||
pub force_install: bool,
|
||||
}
|
||||
|
||||
impl Task for InstallTask {
|
||||
|
@ -42,6 +45,13 @@ impl Task for InstallTask {
|
|||
Box::new(EnsureOnlyInstanceTask {}),
|
||||
));
|
||||
|
||||
if self.force_install {
|
||||
elements.push(TaskDependency::build(
|
||||
TaskOrdering::Pre,
|
||||
Box::new(RemoveTargetDirTask {}),
|
||||
));
|
||||
}
|
||||
|
||||
elements.push(TaskDependency::build(
|
||||
TaskOrdering::Pre,
|
||||
Box::new(VerifyInstallDirTask {
|
||||
|
|
|
@ -1,16 +1,16 @@
|
|||
//! Verifies properties about the installation directory.
|
||||
|
||||
use installer::InstallerFramework;
|
||||
use crate::installer::InstallerFramework;
|
||||
|
||||
use tasks::Task;
|
||||
use tasks::TaskDependency;
|
||||
use tasks::TaskMessage;
|
||||
use tasks::TaskParamType;
|
||||
use crate::tasks::Task;
|
||||
use crate::tasks::TaskDependency;
|
||||
use crate::tasks::TaskMessage;
|
||||
use crate::tasks::TaskParamType;
|
||||
|
||||
use std::fs::create_dir_all;
|
||||
use std::fs::read_dir;
|
||||
|
||||
use logging::LoggingErrors;
|
||||
use crate::logging::LoggingErrors;
|
||||
|
||||
pub struct VerifyInstallDirTask {
|
||||
pub clean_install: bool,
|
||||
|
|
|
@ -1,17 +1,17 @@
|
|||
//! Generates the global shortcut for this application.
|
||||
|
||||
use installer::InstallerFramework;
|
||||
use crate::installer::InstallerFramework;
|
||||
|
||||
use tasks::Task;
|
||||
use tasks::TaskDependency;
|
||||
use tasks::TaskMessage;
|
||||
use tasks::TaskParamType;
|
||||
use crate::tasks::Task;
|
||||
use crate::tasks::TaskDependency;
|
||||
use crate::tasks::TaskMessage;
|
||||
use crate::tasks::TaskParamType;
|
||||
|
||||
use logging::LoggingErrors;
|
||||
use crate::logging::LoggingErrors;
|
||||
|
||||
use native::create_shortcut;
|
||||
use tasks::save_database::SaveDatabaseTask;
|
||||
use tasks::TaskOrdering;
|
||||
use crate::native::create_shortcut;
|
||||
use crate::tasks::save_database::SaveDatabaseTask;
|
||||
use crate::tasks::TaskOrdering;
|
||||
|
||||
pub struct InstallGlobalShortcutsTask {}
|
||||
|
||||
|
|
|
@ -1,26 +1,26 @@
|
|||
//! Installs a specific package.
|
||||
|
||||
use installer::InstallerFramework;
|
||||
use crate::installer::InstallerFramework;
|
||||
|
||||
use tasks::download_pkg::DownloadPackageTask;
|
||||
use tasks::install_shortcuts::InstallShortcutsTask;
|
||||
use tasks::save_database::SaveDatabaseTask;
|
||||
use tasks::uninstall_pkg::UninstallPackageTask;
|
||||
use tasks::Task;
|
||||
use tasks::TaskDependency;
|
||||
use tasks::TaskMessage;
|
||||
use tasks::TaskOrdering;
|
||||
use tasks::TaskParamType;
|
||||
use crate::tasks::download_pkg::DownloadPackageTask;
|
||||
use crate::tasks::install_shortcuts::InstallShortcutsTask;
|
||||
use crate::tasks::save_database::SaveDatabaseTask;
|
||||
use crate::tasks::uninstall_pkg::UninstallPackageTask;
|
||||
use crate::tasks::Task;
|
||||
use crate::tasks::TaskDependency;
|
||||
use crate::tasks::TaskMessage;
|
||||
use crate::tasks::TaskOrdering;
|
||||
use crate::tasks::TaskParamType;
|
||||
|
||||
use config::PackageDescription;
|
||||
use installer::LocalInstallation;
|
||||
use crate::config::PackageDescription;
|
||||
use crate::installer::LocalInstallation;
|
||||
|
||||
use std::fs::create_dir_all;
|
||||
use std::io::copy;
|
||||
|
||||
use logging::LoggingErrors;
|
||||
use crate::logging::LoggingErrors;
|
||||
|
||||
use archives;
|
||||
use crate::archives;
|
||||
|
||||
use std::fs::OpenOptions;
|
||||
use std::path::Path;
|
||||
|
@ -136,7 +136,7 @@ impl Task for InstallPackageTask {
|
|||
info!("Creating file: {:?}", 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();
|
||||
|
@ -165,7 +165,7 @@ impl Task for InstallPackageTask {
|
|||
|
||||
// Save metadata about this package
|
||||
context.database.packages.push(LocalInstallation {
|
||||
name: package.name.to_owned(),
|
||||
name: package.name,
|
||||
version,
|
||||
shortcuts: Vec::new(),
|
||||
files: installed_files,
|
||||
|
|
|
@ -1,17 +1,17 @@
|
|||
//! Generates shortcuts for a specified file.
|
||||
|
||||
use installer::InstallerFramework;
|
||||
use crate::installer::InstallerFramework;
|
||||
|
||||
use tasks::Task;
|
||||
use tasks::TaskDependency;
|
||||
use tasks::TaskMessage;
|
||||
use tasks::TaskParamType;
|
||||
use crate::tasks::Task;
|
||||
use crate::tasks::TaskDependency;
|
||||
use crate::tasks::TaskMessage;
|
||||
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 name: String,
|
||||
|
|
|
@ -4,10 +4,10 @@
|
|||
use std::fmt;
|
||||
use std::fmt::Display;
|
||||
|
||||
use installer::InstallerFramework;
|
||||
use crate::installer::InstallerFramework;
|
||||
|
||||
use sources::types::File;
|
||||
use sources::types::Version;
|
||||
use crate::sources::types::File;
|
||||
use crate::sources::types::Version;
|
||||
|
||||
pub mod check_authorization;
|
||||
pub mod download_pkg;
|
||||
|
@ -26,6 +26,7 @@ pub mod uninstall;
|
|||
pub mod uninstall_global_shortcut;
|
||||
pub mod uninstall_pkg;
|
||||
pub mod uninstall_shortcuts;
|
||||
pub mod remove_target_dir;
|
||||
|
||||
/// An abstraction over the various parameters that can be passed around.
|
||||
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 installer::InstallerFramework;
|
||||
use crate::installer::InstallerFramework;
|
||||
|
||||
use tasks::Task;
|
||||
use tasks::TaskDependency;
|
||||
use tasks::TaskMessage;
|
||||
use tasks::TaskParamType;
|
||||
use crate::tasks::Task;
|
||||
use crate::tasks::TaskDependency;
|
||||
use crate::tasks::TaskMessage;
|
||||
use crate::tasks::TaskParamType;
|
||||
|
||||
use config::PackageDescription;
|
||||
use crate::config::PackageDescription;
|
||||
|
||||
use regex::Regex;
|
||||
|
||||
use logging::LoggingErrors;
|
||||
use crate::logging::LoggingErrors;
|
||||
|
||||
pub struct ResolvePackageTask {
|
||||
pub name: String,
|
||||
|
|
|
@ -1,11 +1,11 @@
|
|||
//! Saves the main database into the installation directory.
|
||||
|
||||
use installer::InstallerFramework;
|
||||
use crate::installer::InstallerFramework;
|
||||
|
||||
use tasks::Task;
|
||||
use tasks::TaskDependency;
|
||||
use tasks::TaskMessage;
|
||||
use tasks::TaskParamType;
|
||||
use crate::tasks::Task;
|
||||
use crate::tasks::TaskDependency;
|
||||
use crate::tasks::TaskMessage;
|
||||
use crate::tasks::TaskParamType;
|
||||
|
||||
pub struct SaveDatabaseTask {}
|
||||
|
||||
|
|
|
@ -1,11 +1,11 @@
|
|||
//! Saves the installer executable into the install directory.
|
||||
|
||||
use installer::InstallerFramework;
|
||||
use crate::installer::InstallerFramework;
|
||||
|
||||
use tasks::Task;
|
||||
use tasks::TaskDependency;
|
||||
use tasks::TaskMessage;
|
||||
use tasks::TaskParamType;
|
||||
use crate::tasks::Task;
|
||||
use crate::tasks::TaskDependency;
|
||||
use crate::tasks::TaskMessage;
|
||||
use crate::tasks::TaskParamType;
|
||||
|
||||
use std::fs::File;
|
||||
use std::fs::OpenOptions;
|
||||
|
@ -14,7 +14,7 @@ use std::io::copy;
|
|||
|
||||
use std::env::current_exe;
|
||||
|
||||
use logging::LoggingErrors;
|
||||
use crate::logging::LoggingErrors;
|
||||
|
||||
pub struct SaveExecutableTask {}
|
||||
|
||||
|
|
|
@ -1,14 +1,14 @@
|
|||
//! Uninstalls a set of packages.
|
||||
|
||||
use installer::InstallerFramework;
|
||||
use crate::installer::InstallerFramework;
|
||||
|
||||
use tasks::Task;
|
||||
use tasks::TaskParamType;
|
||||
use crate::tasks::Task;
|
||||
use crate::tasks::TaskParamType;
|
||||
|
||||
use tasks::uninstall_pkg::UninstallPackageTask;
|
||||
use tasks::TaskDependency;
|
||||
use tasks::TaskMessage;
|
||||
use tasks::TaskOrdering;
|
||||
use crate::tasks::uninstall_pkg::UninstallPackageTask;
|
||||
use crate::tasks::TaskDependency;
|
||||
use crate::tasks::TaskMessage;
|
||||
use crate::tasks::TaskOrdering;
|
||||
|
||||
pub struct UninstallTask {
|
||||
pub items: Vec<String>,
|
||||
|
|
|
@ -1,15 +1,15 @@
|
|||
//! Uninstalls a specific package.
|
||||
|
||||
use installer::InstallerFramework;
|
||||
use crate::installer::InstallerFramework;
|
||||
|
||||
use tasks::Task;
|
||||
use tasks::TaskDependency;
|
||||
use tasks::TaskMessage;
|
||||
use tasks::TaskParamType;
|
||||
use crate::tasks::Task;
|
||||
use crate::tasks::TaskDependency;
|
||||
use crate::tasks::TaskMessage;
|
||||
use crate::tasks::TaskParamType;
|
||||
|
||||
use crate::tasks::save_database::SaveDatabaseTask;
|
||||
use crate::tasks::TaskOrdering;
|
||||
use std::fs::remove_file;
|
||||
use tasks::save_database::SaveDatabaseTask;
|
||||
use tasks::TaskOrdering;
|
||||
|
||||
pub struct UninstallGlobalShortcutsTask {}
|
||||
|
||||
|
|
|
@ -1,21 +1,21 @@
|
|||
//! Uninstalls a specific package.
|
||||
|
||||
use installer::InstallerFramework;
|
||||
use crate::installer::InstallerFramework;
|
||||
|
||||
use tasks::save_database::SaveDatabaseTask;
|
||||
use tasks::Task;
|
||||
use tasks::TaskDependency;
|
||||
use tasks::TaskMessage;
|
||||
use tasks::TaskOrdering;
|
||||
use tasks::TaskParamType;
|
||||
use crate::tasks::save_database::SaveDatabaseTask;
|
||||
use crate::tasks::Task;
|
||||
use crate::tasks::TaskDependency;
|
||||
use crate::tasks::TaskMessage;
|
||||
use crate::tasks::TaskOrdering;
|
||||
use crate::tasks::TaskParamType;
|
||||
|
||||
use installer::LocalInstallation;
|
||||
use crate::installer::LocalInstallation;
|
||||
|
||||
use std::fs::remove_dir;
|
||||
use std::fs::remove_file;
|
||||
|
||||
use logging::LoggingErrors;
|
||||
use tasks::uninstall_shortcuts::UninstallShortcutsTask;
|
||||
use crate::logging::LoggingErrors;
|
||||
use crate::tasks::uninstall_shortcuts::UninstallShortcutsTask;
|
||||
|
||||
pub struct UninstallPackageTask {
|
||||
pub name: String,
|
||||
|
|
|
@ -1,18 +1,18 @@
|
|||
//! Uninstalls a specific package.
|
||||
|
||||
use installer::InstallerFramework;
|
||||
use crate::installer::InstallerFramework;
|
||||
|
||||
use tasks::Task;
|
||||
use tasks::TaskDependency;
|
||||
use tasks::TaskMessage;
|
||||
use tasks::TaskParamType;
|
||||
use crate::tasks::Task;
|
||||
use crate::tasks::TaskDependency;
|
||||
use crate::tasks::TaskMessage;
|
||||
use crate::tasks::TaskParamType;
|
||||
|
||||
use installer::LocalInstallation;
|
||||
use crate::installer::LocalInstallation;
|
||||
|
||||
use std::fs::remove_dir;
|
||||
use std::fs::remove_file;
|
||||
|
||||
use logging::LoggingErrors;
|
||||
use crate::logging::LoggingErrors;
|
||||
|
||||
pub struct UninstallShortcutsTask {
|
||||
pub name: String,
|
||||
|
|
|
@ -3,13 +3,15 @@ module.exports = {
|
|||
env: {
|
||||
node: true
|
||||
},
|
||||
'extends': [
|
||||
extends: [
|
||||
'plugin:vue/essential',
|
||||
'@vue/standard'
|
||||
],
|
||||
rules: {
|
||||
'no-console': process.env.NODE_ENV === 'production' ? 'error' : 'off',
|
||||
'no-debugger': process.env.NODE_ENV === 'production' ? 'error' : 'off'
|
||||
'no-console': 'off',
|
||||
'no-debugger': process.env.NODE_ENV === 'production' ? 'error' : 'off',
|
||||
'no-redeclare': 'off',
|
||||
camelcase: 'off'
|
||||
},
|
||||
parserOptions: {
|
||||
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 port = 3000
|
||||
|
||||
let showError = false
|
||||
let showConfigError = false
|
||||
let maintenance = false
|
||||
let launcher = false
|
||||
let fileExists = false
|
||||
let darkMode = false
|
||||
|
||||
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 timer = setInterval(() => {
|
||||
var resp = JSON.stringify({ Status: ['Processing...', progress] }) + '\n'
|
||||
|
@ -14,10 +27,14 @@ function progressSimulation (res) {
|
|||
res.status(200).end()
|
||||
clearInterval(timer)
|
||||
}
|
||||
}, 1500)
|
||||
}, 500)
|
||||
}
|
||||
|
||||
function returnConfig (res) {
|
||||
if (showConfigError) {
|
||||
res.status(500).json({})
|
||||
return
|
||||
}
|
||||
res.json({
|
||||
installing_message:
|
||||
'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) => {
|
||||
console.log('-- Get attrs')
|
||||
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) => {
|
||||
res.json(false)
|
||||
res.json(darkMode)
|
||||
})
|
||||
|
||||
app.get('/api/installation-status', (req, res) => {
|
||||
res.json({
|
||||
database: { packages: [], shortcuts: [] },
|
||||
install_path: null,
|
||||
preexisting_install: false,
|
||||
is_launcher: false,
|
||||
preexisting_install: maintenance,
|
||||
is_launcher: launcher,
|
||||
launcher_path: null
|
||||
})
|
||||
})
|
||||
|
@ -82,13 +100,54 @@ app.get('/api/config', (req, res) => {
|
|||
})
|
||||
|
||||
app.post('/api/start-install', (req, res) => {
|
||||
console.log(`-- Install: ${req.body}`)
|
||||
console.log(`-- Install: ${req}`)
|
||||
progressSimulation(res)
|
||||
})
|
||||
|
||||
app.get('/api/exit', (req, res) => {
|
||||
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}...`)
|
||||
|
|
|
@ -5,23 +5,32 @@
|
|||
"scripts": {
|
||||
"serve": "vue-cli-service serve",
|
||||
"build": "vue-cli-service build",
|
||||
"lint": "vue-cli-service lint"
|
||||
"lint": "vue-cli-service lint",
|
||||
"postinstall": "node merge-strings.js"
|
||||
},
|
||||
"dependencies": {
|
||||
"buefy": "^0.7.7",
|
||||
"vue": "^2.6.6",
|
||||
"vue-router": "^3.0.1"
|
||||
"@mdi/font": "^5.9.55",
|
||||
"axios": "^0.21.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": {
|
||||
"@vue/cli-plugin-babel": "^3.5.0",
|
||||
"@vue/cli-plugin-eslint": "^3.5.0",
|
||||
"@vue/cli-service": "^3.5.0",
|
||||
"@vue/eslint-config-standard": "^4.0.0",
|
||||
"babel-eslint": "^10.0.1",
|
||||
"eslint": "^5.8.0",
|
||||
"eslint-plugin-vue": "^5.0.0",
|
||||
"@vue/cli-plugin-babel": "^4.5.13",
|
||||
"@vue/cli-plugin-eslint": "^4.5.13",
|
||||
"@vue/cli-service": "^4.5.13",
|
||||
"@vue/eslint-config-standard": "^6.0.0",
|
||||
"babel-eslint": "^10.1.0",
|
||||
"eslint": "^7.28.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",
|
||||
"http-proxy-middleware": "^0.19.1",
|
||||
"vue-template-compiler": "^2.5.21"
|
||||
"http-proxy-middleware": "^2.0.0",
|
||||
"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 http-equiv="X-UA-Compatible" content="IE=11">
|
||||
<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">
|
||||
<title id="window-title">... Installer</title>
|
||||
</head>
|
||||
|
|
|
@ -9,15 +9,23 @@
|
|||
<br />
|
||||
|
||||
<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 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 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>
|
||||
<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>
|
||||
|
||||
<router-view />
|
||||
|
@ -27,6 +35,33 @@
|
|||
</div>
|
||||
</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>
|
||||
/* roboto-regular - latin */
|
||||
@font-face {
|
||||
|
@ -148,7 +183,7 @@ pre {
|
|||
}
|
||||
|
||||
/* 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%);
|
||||
}
|
||||
|
||||
|
|
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.
|
||||
*/
|
||||
|
||||
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,
|
||||
* 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.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.
|
||||
req.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded')
|
||||
|
||||
|
@ -116,7 +62,7 @@ export function stream_ajax (path, callback, successCallback, failCallback, data
|
|||
var form = ''
|
||||
|
||||
for (var key in data) {
|
||||
if (!data.hasOwnProperty(key)) {
|
||||
if (!data[key]) {
|
||||
continue
|
||||
}
|
||||
|
||||
|
@ -132,13 +78,3 @@ export function stream_ajax (path, callback, successCallback, failCallback, data
|
|||
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 App from './App.vue'
|
||||
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 messages from './locales/messages.json'
|
||||
import 'buefy/dist/buefy.css'
|
||||
import '@mdi/font/css/materialdesignicons.min.css'
|
||||
|
||||
Vue.config.productionTip = false
|
||||
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/
|
||||
function intercept (method) {
|
||||
console[method] = function () {
|
||||
var message = Array.prototype.slice.apply(arguments).join(' ')
|
||||
const message = Array.prototype.slice.apply(arguments).join(' ')
|
||||
window.external.invoke(
|
||||
JSON.stringify({
|
||||
Log: {
|
||||
|
@ -24,18 +37,18 @@ function intercept (method) {
|
|||
}
|
||||
|
||||
// See if we have access to the JSON interface
|
||||
var has_external_interface = false;
|
||||
let hasExternalInterface = false
|
||||
try {
|
||||
window.external.invoke(JSON.stringify({
|
||||
Test: {}
|
||||
}))
|
||||
has_external_interface = true;
|
||||
hasExternalInterface = true
|
||||
} 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
|
||||
if (has_external_interface) {
|
||||
if (hasExternalInterface) {
|
||||
window.onerror = function (msg, url, line) {
|
||||
window.external.invoke(
|
||||
JSON.stringify({
|
||||
|
@ -47,14 +60,14 @@ if (has_external_interface) {
|
|||
)
|
||||
}
|
||||
|
||||
var methods = ['log', 'warn', 'error']
|
||||
for (var i = 0; i < methods.length; i++) {
|
||||
const methods = ['log', 'warn', 'error']
|
||||
for (let i = 0; i < methods.length; i++) {
|
||||
intercept(methods[i])
|
||||
}
|
||||
}
|
||||
|
||||
// Disable F5
|
||||
function disable_shortcuts (e) {
|
||||
function disableShortcuts (e) {
|
||||
switch (e.keyCode) {
|
||||
case 116: // F5
|
||||
e.preventDefault()
|
||||
|
@ -63,25 +76,32 @@ function disable_shortcuts (e) {
|
|||
}
|
||||
|
||||
// Check to see if we need to enable dark mode
|
||||
ajax('/api/dark-mode', function (enable) {
|
||||
if (enable) {
|
||||
axios.get('/api/dark-mode').then(function (resp) {
|
||||
if (resp.data === true) {
|
||||
document.body.classList.add('has-background-black-ter')
|
||||
}
|
||||
})
|
||||
|
||||
window.addEventListener('keydown', disable_shortcuts)
|
||||
window.addEventListener('keydown', disableShortcuts)
|
||||
|
||||
document.getElementById('window-title').innerText =
|
||||
base_attributes.name + ' Installer'
|
||||
axios.get('/api/attrs').then(function (resp) {
|
||||
document.getElementById('window-title').innerText =
|
||||
i18n.t('app.window_title', { name: resp.data.name })
|
||||
}).catch(function (err) {
|
||||
console.error(err)
|
||||
})
|
||||
|
||||
function selectFileCallback (name) {
|
||||
app.install_location = name
|
||||
}
|
||||
|
||||
var app = new Vue({
|
||||
window.selectFileCallback = selectFileCallback
|
||||
|
||||
const app = new Vue({
|
||||
i18n: i18n,
|
||||
router: router,
|
||||
data: {
|
||||
attrs: base_attributes,
|
||||
attrs: {},
|
||||
config: {},
|
||||
install_location: '',
|
||||
username: '',
|
||||
|
@ -102,54 +122,61 @@ var app = new Vue({
|
|||
render: function (caller) {
|
||||
return caller(App)
|
||||
},
|
||||
mounted: function () {
|
||||
axios.get('/api/attrs').then(function (resp) {
|
||||
app.attrs = resp.data
|
||||
}).catch(function (err) {
|
||||
console.error(err)
|
||||
})
|
||||
},
|
||||
methods: {
|
||||
exit: function () {
|
||||
ajax(
|
||||
'/api/exit',
|
||||
function () {},
|
||||
function (msg) {
|
||||
var search_location = app.metadata.install_path.length > 0 ? app.metadata.install_path :
|
||||
"the location where this installer is";
|
||||
axios.get('/api/exit').catch(function (msg) {
|
||||
const searchLocation = (app.metadata.install_path && app.metadata.install_path.length > 0)
|
||||
? app.metadata.install_path
|
||||
: i18n.t('error.location_unknown')
|
||||
|
||||
app.$router.replace({ name: 'showerr', params: { msg: msg +
|
||||
'\n\nPlease upload the log file (in ' + search_location + ') to ' +
|
||||
'the ' + app.attrs.name + ' team'
|
||||
}});
|
||||
},
|
||||
{} // pass in nothing to cause `ajax` to post instead of get
|
||||
)
|
||||
app.$router.replace({
|
||||
name: 'showerr',
|
||||
params: {
|
||||
msg: i18n.t('error.exit_error', {
|
||||
name: app.attrs.name,
|
||||
path: searchLocation,
|
||||
msg: msg
|
||||
})
|
||||
}
|
||||
})
|
||||
})
|
||||
},
|
||||
check_authentication: function (success, error) {
|
||||
var that = this;
|
||||
var app = this.$root;
|
||||
const that = this
|
||||
const app = this.$root
|
||||
|
||||
app.ajax('/api/check-auth', function (auth) {
|
||||
app.$data.username = auth.username;
|
||||
app.$data.token = auth.token;
|
||||
that.jwt_token = auth.jwt_token;
|
||||
that.is_authenticated = Object.keys(that.jwt_token).length !== 0 && that.jwt_token.constructor === Object;
|
||||
app.$data.username = auth.username
|
||||
app.$data.token = auth.token
|
||||
that.jwt_token = auth.jwt_token
|
||||
that.is_authenticated = Object.keys(that.jwt_token).length !== 0 && that.jwt_token.constructor === Object
|
||||
if (that.is_authenticated) {
|
||||
// Give all permissions to vip roles
|
||||
that.is_linked = that.jwt_token.isPatreonAccountLinked;
|
||||
that.is_subscribed = that.jwt_token.isPatreonSubscriptionActive;
|
||||
that.has_reward_tier = that.jwt_token.releaseChannels.indexOf("early-access") > -1;
|
||||
that.is_linked = that.jwt_token.isPatreonAccountLinked
|
||||
that.is_subscribed = that.jwt_token.isPatreonSubscriptionActive
|
||||
that.has_reward_tier = that.jwt_token.releaseChannels.indexOf('early-access') > -1
|
||||
}
|
||||
if (success) {
|
||||
success();
|
||||
success()
|
||||
}
|
||||
}, function (e) {
|
||||
if (error) {
|
||||
error();
|
||||
error()
|
||||
}
|
||||
}, {
|
||||
"username": app.$data.username,
|
||||
"token": app.$data.token
|
||||
username: app.$data.username,
|
||||
token: app.$data.token
|
||||
})
|
||||
},
|
||||
|
||||
ajax: ajax,
|
||||
stream_ajax: stream_ajax
|
||||
stream_ajax: streamAjax
|
||||
}
|
||||
}).$mount('#app')
|
||||
|
||||
console.log("Vue started")
|
||||
console.log('Vue started')
|
||||
|
|
|
@ -1,40 +1,40 @@
|
|||
<template>
|
||||
<div class="column has-padding">
|
||||
<div v-if="was_migrate">
|
||||
<h4 class="subtitle">You have been moved to the new, single version of {{ $root.$data.attrs.name }}.</h4>
|
||||
<div v-if="was_migrate">
|
||||
<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"/>
|
||||
</div>
|
||||
<div v-else-if="was_update">
|
||||
<div v-if="has_installed">
|
||||
<h4 class="subtitle">{{ $root.$data.attrs.name }} has been updated.</h4>
|
||||
<img src="../assets/how-to-open.png" alt="Where yuzu is installed"/>
|
||||
</div>
|
||||
<div v-else-if="was_update">
|
||||
<div v-if="has_installed">
|
||||
<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 v-else>
|
||||
<h4 class="subtitle">{{ $root.$data.attrs.name }} is already up to date!</h4>
|
||||
|
||||
<p>You can find your installed applications in your start menu.</p>
|
||||
<h4 class="subtitle">{{ $t('complete.uninstalled', {'name': $root.$data.attrs.name}) }}</h4>
|
||||
</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>
|
||||
<br>
|
||||
<img src="../assets/how-to-open.png" alt="Where yuzu is installed"/>
|
||||
</div>
|
||||
<div v-else>
|
||||
<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 class="field is-grouped is-right-floating is-bottom-floating">
|
||||
<p class="control">
|
||||
<b-button class="is-dark is-medium" v-on:click="exit">{{ $t('exit') }}</b-button>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
|
@ -46,12 +46,12 @@ export default {
|
|||
was_install: !this.$route.params.uninstall,
|
||||
was_update: this.$route.params.update,
|
||||
was_migrate: this.$route.params.migrate,
|
||||
has_installed: this.$route.params.packages_installed > 0,
|
||||
has_installed: this.$route.params.packages_installed > 0
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
exit: function () {
|
||||
this.$root.exit();
|
||||
this.$root.exit()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
<template>
|
||||
<div class="column has-padding">
|
||||
<h4 class="subtitle">Downloading config...</h4>
|
||||
<h4 class="subtitle">{{ $t('download_config.download_config') }}</h4>
|
||||
|
||||
<br />
|
||||
<progress class="progress is-info is-medium" max="100">
|
||||
|
@ -17,51 +17,54 @@ export default {
|
|||
},
|
||||
methods: {
|
||||
download_install_status: function () {
|
||||
var that = this
|
||||
this.$root.ajax('/api/installation-status', function (e) {
|
||||
that.$root.metadata = e
|
||||
const that = this
|
||||
this.$http.get('/api/installation-status').then(function (resp) {
|
||||
that.$root.metadata = resp.data
|
||||
|
||||
that.download_config()
|
||||
})
|
||||
},
|
||||
download_config: function () {
|
||||
var that = this
|
||||
this.$root.ajax('/api/config', function (e) {
|
||||
that.$root.config = e
|
||||
|
||||
// Update the updater if needed
|
||||
if (that.$root.config.new_tool) {
|
||||
that.$router.push('/install/updater/false')
|
||||
return
|
||||
}
|
||||
const that = this
|
||||
this.$http.get('/api/config').then(function (resp) {
|
||||
that.$root.config = resp.data
|
||||
|
||||
that.$root.check_authentication(that.choose_next_state, that.choose_next_state)
|
||||
}, function (e) {
|
||||
console.error('Got error while downloading config: ' + e)
|
||||
}).catch(function (e) {
|
||||
console.error('Got error while downloading config: ' +
|
||||
e)
|
||||
|
||||
if (that.$root.metadata.is_launcher) {
|
||||
// Just launch the target application
|
||||
that.$root.exit()
|
||||
} else {
|
||||
that.$router.replace({ name: 'showerr',
|
||||
params: { msg: 'Got error while downloading config: ' + e } })
|
||||
that.$router.replace({
|
||||
name: 'showerr',
|
||||
params: { msg: that.$i18n.t('download_config.error_download_config', { msg: e }) }
|
||||
})
|
||||
}
|
||||
})
|
||||
},
|
||||
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) {
|
||||
app.install_location = app.metadata.install_path
|
||||
|
||||
// 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].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
|
||||
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) {
|
||||
app.config.packages[x].default = true
|
||||
app.config.packages[x].installed = true
|
||||
|
@ -69,23 +72,27 @@ export default {
|
|||
}
|
||||
}
|
||||
|
||||
this.$router.replace({ name: 'migrate',
|
||||
params: { next: app.metadata.is_launcher ? '/install/regular/false' : '/modify' } })
|
||||
this.$router.replace({
|
||||
name: 'migrate',
|
||||
params: { next: app.metadata.is_launcher ? '/install/regular/false' : '/modify' }
|
||||
})
|
||||
} 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
|
||||
}
|
||||
|
||||
// Need to do a bit more digging to get at the
|
||||
// install location.
|
||||
this.$root.ajax('/api/default-path', function (e) {
|
||||
if (e.path != null) {
|
||||
app.install_location = e.path
|
||||
this.$http.get('/api/default-path').then(function (resp) {
|
||||
if (resp.data.path != null) {
|
||||
app.install_location = resp.data.path
|
||||
}
|
||||
})
|
||||
|
||||
this.$router.replace({ name: 'migrate',
|
||||
params: { next: '/packages' } })
|
||||
this.$router.replace({
|
||||
name: 'migrate',
|
||||
params: { next: '/packages' }
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,12 +1,12 @@
|
|||
<template>
|
||||
<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>
|
||||
</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">
|
||||
<a class="button is-primary is-medium" v-if="remaining && !$root.$data.metadata.is_launcher" v-on:click="go_back">Back</a>
|
||||
<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="remaining && !$root.$data.metadata.is_launcher" v-on:click="go_back">{{ $t('back') }}</b-button>
|
||||
<b-button class="is-primary is-medium" v-if="$root.$data.metadata.is_launcher" v-on:click="exit">{{ $t('exit') }}</b-button>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -38,12 +38,12 @@ export default {
|
|||
return {
|
||||
// https://stackoverflow.com/questions/6234773/can-i-escape-html-special-chars-in-javascript
|
||||
msg: this.$route.params.msg
|
||||
.replace(/&/g, "&")
|
||||
.replace(/</g, "<")
|
||||
.replace(/>/g, ">")
|
||||
.replace(/"/g, """)
|
||||
.replace(/'/g, "'")
|
||||
.replace(/\n/g, "<br />"),
|
||||
.replace(/&/g, '&')
|
||||
.replace(/</g, '<')
|
||||
.replace(/>/g, '>')
|
||||
.replace(/"/g, '"')
|
||||
.replace(/'/g, ''')
|
||||
.replace(/\n/g, '<br />'),
|
||||
remaining: window.history.length > 1
|
||||
}
|
||||
},
|
||||
|
|
|
@ -1,9 +1,9 @@
|
|||
<template>
|
||||
<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-else-if="is_uninstall">Uninstalling...</h4>
|
||||
<h4 class="subtitle" v-else-if="is_updater_update">Downloading self-update...</h4>
|
||||
<h4 class="subtitle" v-else>Installing...</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">{{ $t('install_packages.uninstall') }}</h4>
|
||||
<h4 class="subtitle" v-else-if="is_updater_update">{{ $t('install_packages.self_update') }}</h4>
|
||||
<h4 class="subtitle" v-else>{{ $t('install_packages.install') }}</h4>
|
||||
<div v-html="$root.$data.config.installing_message"></div>
|
||||
<br />
|
||||
|
||||
|
@ -20,10 +20,11 @@ export default {
|
|||
data: function () {
|
||||
return {
|
||||
progress: 0.0,
|
||||
progress_message: 'Please wait...',
|
||||
progress_message: this.$i18n.t('install_packages.please_wait'),
|
||||
is_uninstall: false,
|
||||
is_updater_update: false,
|
||||
is_update: false,
|
||||
is_repair: false,
|
||||
install_desktop_shortcut: false,
|
||||
failed_with_error: false,
|
||||
authorization_required: false,
|
||||
|
@ -35,30 +36,33 @@ export default {
|
|||
this.is_updater_update = this.$route.params.kind === 'updater'
|
||||
this.is_update = this.$route.params.kind === 'update'
|
||||
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('Installing desktop shortcut: ' + this.$route.params.desktop_shortcut)
|
||||
this.install()
|
||||
},
|
||||
methods: {
|
||||
install: function () {
|
||||
var that = this
|
||||
var app = this.$root
|
||||
const that = this
|
||||
const app = this.$root
|
||||
|
||||
var results = {}
|
||||
var requires_authorization = false;
|
||||
const results = {}
|
||||
|
||||
for (var package_index = 0; package_index < app.config.packages.length; package_index++) {
|
||||
var current_package = app.config.packages[package_index]
|
||||
for (let package_index = 0; package_index < app.config.packages.length; package_index++) {
|
||||
const current_package = app.config.packages[package_index]
|
||||
if (current_package.default != null) {
|
||||
requires_authorization |= current_package.requires_authorization;
|
||||
results[current_package.name] = current_package.default
|
||||
}
|
||||
}
|
||||
|
||||
results['path'] = app.install_location
|
||||
results['installDesktopShortcut'] = that.install_desktop_shortcut
|
||||
results.path = app.install_location
|
||||
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) {
|
||||
targetUrl = '/api/uninstall'
|
||||
}
|
||||
|
@ -69,20 +73,20 @@ export default {
|
|||
this.$root.stream_ajax(targetUrl, function (line) {
|
||||
// On progress line received from server
|
||||
|
||||
if (line.hasOwnProperty('Status')) {
|
||||
if (line.Status) {
|
||||
that.progress_message = line.Status[0]
|
||||
that.progress = line.Status[1] * 100
|
||||
}
|
||||
|
||||
if (line.hasOwnProperty('PackageInstalled')) {
|
||||
if (line.PackageInstalled) {
|
||||
that.packages_installed += 1
|
||||
}
|
||||
|
||||
if (line.hasOwnProperty('AuthorizationRequired')) {
|
||||
if (line.AuthorizationRequired) {
|
||||
that.authorization_required = true
|
||||
}
|
||||
|
||||
if (line.hasOwnProperty('Error')) {
|
||||
if (line.Error) {
|
||||
that.failed_with_error = true
|
||||
that.$router.replace({ name: 'showerr', params: { msg: line.Error } })
|
||||
}
|
||||
|
@ -107,21 +111,23 @@ export default {
|
|||
app.exit()
|
||||
} else if (!that.failed_with_error) {
|
||||
if (that.is_uninstall) {
|
||||
that.$router.replace({ name: 'complete',
|
||||
that.$router.replace({
|
||||
name: 'complete',
|
||||
params: {
|
||||
uninstall: true,
|
||||
update: that.is_update,
|
||||
migrate: false,
|
||||
installed: that.packages_installed
|
||||
} })
|
||||
}
|
||||
})
|
||||
} else {
|
||||
that.$router.replace({ name: 'complete',
|
||||
that.$router.replace({
|
||||
name: 'complete',
|
||||
params: {
|
||||
uninstall: false,
|
||||
update: that.is_update,
|
||||
migrate: false,
|
||||
installed: that.packages_installed
|
||||
} })
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,35 +1,34 @@
|
|||
<template>
|
||||
<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">
|
||||
Update
|
||||
</a>
|
||||
<b-button icon-left="update" type="is-dark-green" size="is-medium" @click="update">
|
||||
{{ $t('modify.update') }}
|
||||
</b-button>
|
||||
<br />
|
||||
<br />
|
||||
|
||||
<a class="button is-dark is-medium" v-on:click="modify_packages">
|
||||
Modify
|
||||
</a>
|
||||
<b-button icon-left="pencil" type="is-info" size="is-medium" @click="modify_packages">
|
||||
{{ $t('modify.modify') }}
|
||||
</b-button>
|
||||
<br />
|
||||
<br />
|
||||
|
||||
<a class="button is-dark is-medium" v-on:click="prepare_uninstall">
|
||||
Uninstall
|
||||
</a>
|
||||
<b-button icon-left="wrench" type="is-info" size="is-medium" @click="repair_packages">
|
||||
{{ $t('modify.repair') }}
|
||||
</b-button>
|
||||
<br />
|
||||
<br />
|
||||
|
||||
<div class="modal is-active" v-if="show_uninstall">
|
||||
<div class="modal-background"></div>
|
||||
<div class="modal-card">
|
||||
<header class="modal-card-head">
|
||||
<p class="modal-card-title">Are you sure you want to uninstall {{ $root.$data.attrs.name }}?</p>
|
||||
</header>
|
||||
<footer class="modal-card-foot">
|
||||
<button class="button is-danger" v-on:click="uninstall">Yes</button>
|
||||
<button class="button" v-on:click="cancel_uninstall">No</button>
|
||||
</footer>
|
||||
</div>
|
||||
</div>
|
||||
<b-button icon-left="delete" type="is-danger" size="is-medium" @click="prepare_uninstall">
|
||||
{{ $t('modify.uninstall') }}
|
||||
</b-button>
|
||||
<br />
|
||||
<br />
|
||||
|
||||
<b-button icon-left="file-find" type="is-link" size="is-medium" @click="view_files">
|
||||
{{ $t('modify.view_local_files') }}
|
||||
</b-button>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
|
@ -37,9 +36,7 @@
|
|||
export default {
|
||||
name: 'ModifyView',
|
||||
data: function () {
|
||||
return {
|
||||
show_uninstall: false
|
||||
}
|
||||
return {}
|
||||
},
|
||||
methods: {
|
||||
update: function () {
|
||||
|
@ -48,15 +45,42 @@ export default {
|
|||
modify_packages: function () {
|
||||
this.$router.push('/packages')
|
||||
},
|
||||
prepare_uninstall: function () {
|
||||
this.show_uninstall = true
|
||||
repair_packages: function () {
|
||||
this.$router.push({ name: 'packages', params: { repair: true } })
|
||||
},
|
||||
cancel_uninstall: function () {
|
||||
this.show_uninstall = false
|
||||
prepare_uninstall: function () {
|
||||
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 () {
|
||||
this.$router.push('/install/uninstall/false')
|
||||
},
|
||||
view_files: function () {
|
||||
this.$http.get('/api/view-local-folder')
|
||||
}
|
||||
}
|
||||
}
|
||||
</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>
|
||||
<div class="column has-padding">
|
||||
<!-- Build options -->
|
||||
<div class="tile is-ancestor">
|
||||
<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)">
|
||||
<div class="ribbon" v-if="Lpackage.is_new"><span>New!</span></div>
|
||||
<label class="checkbox">
|
||||
<b-checkbox v-model="Lpackage.default">
|
||||
<span v-if="!Lpackage.installed">Install</span> {{ Lpackage.name }}
|
||||
</b-checkbox>
|
||||
<span v-if="Lpackage.installed"><i>(installed)</i></span>
|
||||
</label>
|
||||
<div>
|
||||
<img class="package-icon" :src="`${publicPath + Lpackage.icon}`"/>
|
||||
<p style="padding-top: 4px;" class="package-description">
|
||||
{{ Lpackage.description }}
|
||||
</p>
|
||||
<p class="package-description">
|
||||
{{ get_extended_description(Lpackage) }}
|
||||
</p>
|
||||
</div>
|
||||
<div class="column has-padding">
|
||||
<h4 class="subtitle" v-if="!repair">{{ $t('select_packages.title') }}</h4>
|
||||
<h4 class="subtitle" v-if="repair">{{ $t('select_packages.title_repair') }}</h4>
|
||||
|
||||
<!-- Build options -->
|
||||
<div class="tile is-ancestor">
|
||||
<div class="tile is-parent" v-for="Lpackage in $root.$data.config.packages" :key="Lpackage.name" :index="Lpackage.name">
|
||||
<div class="tile is-child">
|
||||
<div class="box clickable-box" v-on:click.capture.stop="Lpackage.default = !Lpackage.default">
|
||||
<div class="ribbon" v-if="Lpackage.is_new"><span>New!</span></div>
|
||||
<label class="checkbox">
|
||||
<b-checkbox v-model="Lpackage.default">
|
||||
{{ Lpackage.name }}
|
||||
</b-checkbox>
|
||||
<span v-if="Lpackage.installed"><i>{{ $t('select_packages.installed') }}</i></span>
|
||||
</label>
|
||||
<div>
|
||||
<img class="package-icon" :src="`${publicPath + Lpackage.icon}`"/>
|
||||
<p style="padding-top: 4px;" class="package-description">
|
||||
{{ Lpackage.description }}
|
||||
</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 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 class="subtitle is-6" v-if="!$root.$data.metadata.preexisting_install && advanced">{{ $t('select_packages.location') }}</div>
|
||||
<div class="field has-addons" v-if="!$root.$data.metadata.preexisting_install && advanced">
|
||||
<div class="control is-expanded">
|
||||
<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 class="subtitle is-6" v-if="!$root.$data.metadata.preexisting_install && advanced">Install Location</div>
|
||||
<div class="field has-addons" v-if="!$root.$data.metadata.preexisting_install && advanced">
|
||||
<div class="control is-expanded">
|
||||
<input class="input" type="text" v-model="$root.$data.install_location"
|
||||
placeholder="Enter a install path here">
|
||||
</div>
|
||||
<div class="control">
|
||||
<a class="button is-dark" v-on:click="select_file">
|
||||
Select
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
<div class="is-right-floating is-bottom-floating">
|
||||
<div class="field is-grouped">
|
||||
<p class="control">
|
||||
<b-button class="is-medium" v-if="!$root.$data.config.hide_advanced && !$root.$data.metadata.preexisting_install && !advanced"
|
||||
v-on:click="advanced = true">{{ $t('select_packages.advanced') }}</b-button>
|
||||
</p>
|
||||
<p class="control">
|
||||
<b-button class="is-dark is-medium" v-if="!$root.$data.metadata.preexisting_install"
|
||||
v-on:click="install">{{ $t('select_packages.install') }}</b-button>
|
||||
</p>
|
||||
<p class="control">
|
||||
<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">
|
||||
<p class="control">
|
||||
<a class="button is-medium" v-if="!$root.$data.config.hide_advanced && !$root.$data.metadata.preexisting_install && !advanced"
|
||||
v-on:click="advanced = true">Advanced...</a>
|
||||
</p>
|
||||
<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 class="field is-grouped is-left-floating is-bottom-floating">
|
||||
<p class="control">
|
||||
<b-button class="is-medium" v-if="$root.$data.metadata.preexisting_install"
|
||||
v-on:click="go_back">{{ $t('back') }}</b-button>
|
||||
</p>
|
||||
</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>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: 'SelectPackages',
|
||||
created: function() {
|
||||
// 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++) {
|
||||
let current_package = this.$root.config.packages[package_index];
|
||||
current_package.default = current_package.requires_authorization;
|
||||
export default {
|
||||
name: 'SelectPackages',
|
||||
data: function () {
|
||||
return {
|
||||
advanced: false,
|
||||
repair: false,
|
||||
installDesktopShortcut: true
|
||||
}
|
||||
},
|
||||
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 () {
|
||||
return {
|
||||
publicPath: process.env.BASE_URL,
|
||||
advanced: false,
|
||||
installDesktopShortcut: true
|
||||
get_extended_description: function (pkg) {
|
||||
if (!pkg.extended_description) {
|
||||
return ''
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
has_package_selected: function() {
|
||||
for (let i=0; i < this.$root.config.packages.length; ++i) {
|
||||
let pkg = this.$root.config.packages[i];
|
||||
if (pkg.default) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
},
|
||||
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;
|
||||
}
|
||||
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>
|
||||
|
|
7789
ui/yarn.lock
7789
ui/yarn.lock
File diff suppressed because it is too large
Load diff
Loading…
Reference in a new issue