mirror of
				https://github.com/yuzu-emu/liftinstall.git
				synced 2025-11-04 04:35:07 +00:00 
			
		
		
		
	Dynamically fetch configuration file
This commit is contained in:
		
							parent
							
								
									08cf5dea6f
								
							
						
					
					
						commit
						b32e9f6f33
					
				
							
								
								
									
										31
									
								
								config.toml
									
									
									
									
									
								
							
							
						
						
									
										31
									
								
								config.toml
									
									
									
									
									
								
							| 
						 | 
					@ -1,31 +1,2 @@
 | 
				
			||||||
[general]
 | 
					 | 
				
			||||||
name = "yuzu"
 | 
					name = "yuzu"
 | 
				
			||||||
installing_message = "Reminder: yuzu is an <b>experimental</b> emulator. Stuff will break!"
 | 
					target_url = "https://raw.githubusercontent.com/j-selby/test-installer/master/config.v1.toml"
 | 
				
			||||||
 | 
					 | 
				
			||||||
[[packages]]
 | 
					 | 
				
			||||||
name = "yuzu Nightly"
 | 
					 | 
				
			||||||
description = "The nightly build of yuzu contains already reviewed and tested features."
 | 
					 | 
				
			||||||
    [packages.source]
 | 
					 | 
				
			||||||
    name = "github"
 | 
					 | 
				
			||||||
    match = "^yuzu-#PLATFORM#(-mingw)?-[0-9]*-[0-9a-f]*.zip$"
 | 
					 | 
				
			||||||
        [packages.source.config]
 | 
					 | 
				
			||||||
        repo = "yuzu-emu/yuzu-nightly"
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
[[packages]]
 | 
					 | 
				
			||||||
name = "yuzu Canary"
 | 
					 | 
				
			||||||
description = "The canary build of yuzu has additional features that are still waiting on review."
 | 
					 | 
				
			||||||
    [packages.source]
 | 
					 | 
				
			||||||
    name = "github"
 | 
					 | 
				
			||||||
    match = "^yuzu-#PLATFORM#(-mingw)?-[0-9]*-[0-9a-f]*.zip$"
 | 
					 | 
				
			||||||
        [packages.source.config]
 | 
					 | 
				
			||||||
        repo = "yuzu-emu/yuzu-canary"
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
[[packages]]
 | 
					 | 
				
			||||||
name = "Test package"
 | 
					 | 
				
			||||||
description = "Just a testing package"
 | 
					 | 
				
			||||||
default = true
 | 
					 | 
				
			||||||
    [packages.source]
 | 
					 | 
				
			||||||
    name = "github"
 | 
					 | 
				
			||||||
    match = "^TestPackage.zip$"
 | 
					 | 
				
			||||||
        [packages.source.config]
 | 
					 | 
				
			||||||
        repo = "j-selby/test-installer"
 | 
					 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -30,14 +30,26 @@ pub struct PackageDescription {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/// Describes the application itself.
 | 
					/// Describes the application itself.
 | 
				
			||||||
#[derive(Debug, Deserialize, Serialize, Clone)]
 | 
					#[derive(Debug, Deserialize, Serialize, Clone)]
 | 
				
			||||||
pub struct GeneralConfig {
 | 
					pub struct BaseAttributes {
 | 
				
			||||||
    pub name: String,
 | 
					    pub name: String,
 | 
				
			||||||
    pub installing_message: String,
 | 
					    pub target_url: String,
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					impl BaseAttributes {
 | 
				
			||||||
 | 
					    /// Serialises as a JSON string.
 | 
				
			||||||
 | 
					    pub fn to_json_str(&self) -> Result<String, SerdeError> {
 | 
				
			||||||
 | 
					        serde_json::to_string(self)
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /// Builds a configuration from a specified TOML string.
 | 
				
			||||||
 | 
					    pub fn from_toml_str(contents: &str) -> Result<Self, TomlError> {
 | 
				
			||||||
 | 
					        toml::from_str(contents)
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#[derive(Debug, Deserialize, Serialize, Clone)]
 | 
					#[derive(Debug, Deserialize, Serialize, Clone)]
 | 
				
			||||||
pub struct Config {
 | 
					pub struct Config {
 | 
				
			||||||
    pub general: GeneralConfig,
 | 
					    pub installing_message: String,
 | 
				
			||||||
    pub packages: Vec<PackageDescription>,
 | 
					    pub packages: Vec<PackageDescription>,
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
							
								
								
									
										13
									
								
								src/http.rs
									
									
									
									
									
								
							
							
						
						
									
										13
									
								
								src/http.rs
									
									
									
									
									
								
							| 
						 | 
					@ -8,6 +8,19 @@ use reqwest;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
use std::io::Read;
 | 
					use std::io::Read;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/// Downloads a text file from the specified URL.
 | 
				
			||||||
 | 
					pub fn download_text(url: &str) -> Result<String, String> {
 | 
				
			||||||
 | 
					    // TODO: Decrease check time
 | 
				
			||||||
 | 
					    let mut client = match reqwest::get(url) {
 | 
				
			||||||
 | 
					        Ok(v) => v,
 | 
				
			||||||
 | 
					        Err(v) => return Err(format!("Failed to GET resource: {:?}", v)),
 | 
				
			||||||
 | 
					    };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    client
 | 
				
			||||||
 | 
					        .text()
 | 
				
			||||||
 | 
					        .map_err(|v| format!("Failed to get text from resource: {:?}", v))
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/// Streams a file from a HTTP server.
 | 
					/// Streams a file from a HTTP server.
 | 
				
			||||||
pub fn stream_file<F>(url: &str, mut callback: F) -> Result<(), String>
 | 
					pub fn stream_file<F>(url: &str, mut callback: F) -> Result<(), String>
 | 
				
			||||||
where
 | 
					where
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -13,6 +13,7 @@ use std::path::PathBuf;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
use std::sync::mpsc::Sender;
 | 
					use std::sync::mpsc::Sender;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					use config::BaseAttributes;
 | 
				
			||||||
use config::Config;
 | 
					use config::Config;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
use sources::types::Version;
 | 
					use sources::types::Version;
 | 
				
			||||||
| 
						 | 
					@ -36,7 +37,8 @@ pub enum InstallMessage {
 | 
				
			||||||
/// The installer framework contains metadata about packages, what is installable, what isn't,
 | 
					/// The installer framework contains metadata about packages, what is installable, what isn't,
 | 
				
			||||||
/// etc.
 | 
					/// etc.
 | 
				
			||||||
pub struct InstallerFramework {
 | 
					pub struct InstallerFramework {
 | 
				
			||||||
    pub config: Config,
 | 
					    pub base_attributes: BaseAttributes,
 | 
				
			||||||
 | 
					    pub config: Option<Config>,
 | 
				
			||||||
    pub database: Vec<LocalInstallation>,
 | 
					    pub database: Vec<LocalInstallation>,
 | 
				
			||||||
    pub install_path: Option<PathBuf>,
 | 
					    pub install_path: Option<PathBuf>,
 | 
				
			||||||
    pub preexisting_install: bool,
 | 
					    pub preexisting_install: bool,
 | 
				
			||||||
| 
						 | 
					@ -64,13 +66,13 @@ pub struct LocalInstallation {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
impl InstallerFramework {
 | 
					impl InstallerFramework {
 | 
				
			||||||
    /// Returns a copy of the configuration.
 | 
					    /// Returns a copy of the configuration.
 | 
				
			||||||
    pub fn get_config(&self) -> Config {
 | 
					    pub fn get_config(&self) -> Option<Config> {
 | 
				
			||||||
        self.config.clone()
 | 
					        self.config.clone()
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    /// Returns the default install path.
 | 
					    /// Returns the default install path.
 | 
				
			||||||
    pub fn get_default_path(&self) -> Option<String> {
 | 
					    pub fn get_default_path(&self) -> Option<String> {
 | 
				
			||||||
        let app_name = &self.config.general.name;
 | 
					        let app_name = &self.base_attributes.name;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        let base_dir = match var("LOCALAPPDATA") {
 | 
					        let base_dir = match var("LOCALAPPDATA") {
 | 
				
			||||||
            Ok(path) => PathBuf::from(path),
 | 
					            Ok(path) => PathBuf::from(path),
 | 
				
			||||||
| 
						 | 
					@ -194,9 +196,10 @@ impl InstallerFramework {
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    /// Creates a new instance of the Installer Framework with a specified Config.
 | 
					    /// Creates a new instance of the Installer Framework with a specified Config.
 | 
				
			||||||
    pub fn new(config: Config) -> Self {
 | 
					    pub fn new(attrs: BaseAttributes) -> Self {
 | 
				
			||||||
        InstallerFramework {
 | 
					        InstallerFramework {
 | 
				
			||||||
            config,
 | 
					            base_attributes: attrs,
 | 
				
			||||||
 | 
					            config: None,
 | 
				
			||||||
            database: Vec::new(),
 | 
					            database: Vec::new(),
 | 
				
			||||||
            install_path: None,
 | 
					            install_path: None,
 | 
				
			||||||
            preexisting_install: false,
 | 
					            preexisting_install: false,
 | 
				
			||||||
| 
						 | 
					@ -207,7 +210,7 @@ impl InstallerFramework {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    /// Creates a new instance of the Installer Framework with a specified Config, managing
 | 
					    /// Creates a new instance of the Installer Framework with a specified Config, managing
 | 
				
			||||||
    /// a pre-existing installation.
 | 
					    /// a pre-existing installation.
 | 
				
			||||||
    pub fn new_with_db(config: Config, install_path: &Path) -> Result<Self, String> {
 | 
					    pub fn new_with_db(attrs: BaseAttributes, install_path: &Path) -> Result<Self, String> {
 | 
				
			||||||
        let path = install_path.to_owned();
 | 
					        let path = install_path.to_owned();
 | 
				
			||||||
        let metadata_path = path.join("metadata.json");
 | 
					        let metadata_path = path.join("metadata.json");
 | 
				
			||||||
        let metadata_file = match File::open(metadata_path) {
 | 
					        let metadata_file = match File::open(metadata_path) {
 | 
				
			||||||
| 
						 | 
					@ -221,7 +224,8 @@ impl InstallerFramework {
 | 
				
			||||||
        };
 | 
					        };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        Ok(InstallerFramework {
 | 
					        Ok(InstallerFramework {
 | 
				
			||||||
            config,
 | 
					            base_attributes: attrs,
 | 
				
			||||||
 | 
					            config: None,
 | 
				
			||||||
            database,
 | 
					            database,
 | 
				
			||||||
            install_path: Some(path),
 | 
					            install_path: Some(path),
 | 
				
			||||||
            preexisting_install: true,
 | 
					            preexisting_install: true,
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -17,7 +17,8 @@ pub fn setup_logger() -> Result<(), fern::InitError> {
 | 
				
			||||||
                record.level(),
 | 
					                record.level(),
 | 
				
			||||||
                message
 | 
					                message
 | 
				
			||||||
            ))
 | 
					            ))
 | 
				
			||||||
        }).level(log::LevelFilter::Info)
 | 
					        })
 | 
				
			||||||
 | 
					        .level(log::LevelFilter::Info)
 | 
				
			||||||
        .chain(io::stdout())
 | 
					        .chain(io::stdout())
 | 
				
			||||||
        .chain(fern::log_file("installer.log")?)
 | 
					        .chain(fern::log_file("installer.log")?)
 | 
				
			||||||
        .apply()?;
 | 
					        .apply()?;
 | 
				
			||||||
| 
						 | 
					@ -31,6 +32,7 @@ where
 | 
				
			||||||
    Self: Sized,
 | 
					    Self: Sized,
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
    /// Unwraps this object. See `unwrap()`.
 | 
					    /// Unwraps this object. See `unwrap()`.
 | 
				
			||||||
 | 
					    #[inline]
 | 
				
			||||||
    fn log_unwrap(self) -> T {
 | 
					    fn log_unwrap(self) -> T {
 | 
				
			||||||
        self.log_expect("Failed to unwrap")
 | 
					        self.log_expect("Failed to unwrap")
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
| 
						 | 
					@ -40,6 +42,7 @@ where
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
impl<T, E: Debug> LoggingErrors<T> for Result<T, E> {
 | 
					impl<T, E: Debug> LoggingErrors<T> for Result<T, E> {
 | 
				
			||||||
 | 
					    #[inline]
 | 
				
			||||||
    fn log_expect(self, msg: &str) -> T {
 | 
					    fn log_expect(self, msg: &str) -> T {
 | 
				
			||||||
        match self {
 | 
					        match self {
 | 
				
			||||||
            Ok(v) => v,
 | 
					            Ok(v) => v,
 | 
				
			||||||
| 
						 | 
					@ -52,6 +55,7 @@ impl<T, E: Debug> LoggingErrors<T> for Result<T, E> {
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
impl<T> LoggingErrors<T> for Option<T> {
 | 
					impl<T> LoggingErrors<T> for Option<T> {
 | 
				
			||||||
 | 
					    #[inline]
 | 
				
			||||||
    fn log_expect(self, msg: &str) -> T {
 | 
					    fn log_expect(self, msg: &str) -> T {
 | 
				
			||||||
        match self {
 | 
					        match self {
 | 
				
			||||||
            Some(v) => v,
 | 
					            Some(v) => v,
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
							
								
								
									
										12
									
								
								src/main.rs
									
									
									
									
									
								
							
							
						
						
									
										12
									
								
								src/main.rs
									
									
									
									
									
								
							| 
						 | 
					@ -50,8 +50,6 @@ mod tasks;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
use web_view::*;
 | 
					use web_view::*;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
use config::Config;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
use installer::InstallerFramework;
 | 
					use installer::InstallerFramework;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#[cfg(windows)]
 | 
					#[cfg(windows)]
 | 
				
			||||||
| 
						 | 
					@ -71,6 +69,8 @@ use clap::App;
 | 
				
			||||||
use clap::Arg;
 | 
					use clap::Arg;
 | 
				
			||||||
use log::Level;
 | 
					use log::Level;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					use config::BaseAttributes;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// TODO: Fetch this over a HTTP request?
 | 
					// TODO: Fetch this over a HTTP request?
 | 
				
			||||||
static RAW_CONFIG: &'static str = include_str!("../config.toml");
 | 
					static RAW_CONFIG: &'static str = include_str!("../config.toml");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -83,9 +83,10 @@ enum CallbackType {
 | 
				
			||||||
fn main() {
 | 
					fn main() {
 | 
				
			||||||
    logging::setup_logger().expect("Unable to setup logging!");
 | 
					    logging::setup_logger().expect("Unable to setup logging!");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    let config = Config::from_toml_str(RAW_CONFIG).log_expect("Config file could not be read");
 | 
					    let config =
 | 
				
			||||||
 | 
					        BaseAttributes::from_toml_str(RAW_CONFIG).log_expect("Config file could not be read");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    let app_name = config.general.name.clone();
 | 
					    let app_name = config.name.clone();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    let matches = App::new(format!("{} installer", app_name))
 | 
					    let matches = App::new(format!("{} installer", app_name))
 | 
				
			||||||
        .version(env!("CARGO_PKG_VERSION"))
 | 
					        .version(env!("CARGO_PKG_VERSION"))
 | 
				
			||||||
| 
						 | 
					@ -96,7 +97,8 @@ fn main() {
 | 
				
			||||||
                .value_name("TARGET")
 | 
					                .value_name("TARGET")
 | 
				
			||||||
                .help("Launches the specified executable after checking for updates")
 | 
					                .help("Launches the specified executable after checking for updates")
 | 
				
			||||||
                .takes_value(true),
 | 
					                .takes_value(true),
 | 
				
			||||||
        ).get_matches();
 | 
					        )
 | 
				
			||||||
 | 
					        .get_matches();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    info!("{} installer", app_name);
 | 
					    info!("{} installer", app_name);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
							
								
								
									
										66
									
								
								src/rest.rs
									
									
									
									
									
								
							
							
						
						
									
										66
									
								
								src/rest.rs
									
									
									
									
									
								
							| 
						 | 
					@ -32,6 +32,10 @@ use installer::InstallerFramework;
 | 
				
			||||||
use logging::LoggingErrors;
 | 
					use logging::LoggingErrors;
 | 
				
			||||||
use std::process::Command;
 | 
					use std::process::Command;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					use http;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					use config::Config;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#[derive(Serialize)]
 | 
					#[derive(Serialize)]
 | 
				
			||||||
struct FileSelection {
 | 
					struct FileSelection {
 | 
				
			||||||
    path: Option<String>,
 | 
					    path: Option<String>,
 | 
				
			||||||
| 
						 | 
					@ -55,7 +59,8 @@ impl WebServer {
 | 
				
			||||||
                    Ok(WebService {
 | 
					                    Ok(WebService {
 | 
				
			||||||
                        framework: framework.clone(),
 | 
					                        framework: framework.clone(),
 | 
				
			||||||
                    })
 | 
					                    })
 | 
				
			||||||
                }).log_expect("Failed to bind to port");
 | 
					                })
 | 
				
			||||||
 | 
					                .log_expect("Failed to bind to port");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            server.run().log_expect("Failed to run HTTP server");
 | 
					            server.run().log_expect("Failed to run HTTP server");
 | 
				
			||||||
        });
 | 
					        });
 | 
				
			||||||
| 
						 | 
					@ -79,16 +84,16 @@ impl Service for WebService {
 | 
				
			||||||
    fn call(&self, req: Self::Request) -> Self::Future {
 | 
					    fn call(&self, req: Self::Request) -> Self::Future {
 | 
				
			||||||
        Box::new(future::ok(match (req.method(), req.path()) {
 | 
					        Box::new(future::ok(match (req.method(), req.path()) {
 | 
				
			||||||
            // This endpoint should be usable directly from a <script> tag during loading.
 | 
					            // This endpoint should be usable directly from a <script> tag during loading.
 | 
				
			||||||
            (&Get, "/api/config") => {
 | 
					            (&Get, "/api/attrs") => {
 | 
				
			||||||
                let framework = self
 | 
					                let framework = self
 | 
				
			||||||
                    .framework
 | 
					                    .framework
 | 
				
			||||||
                    .read()
 | 
					                    .read()
 | 
				
			||||||
                    .log_expect("InstallerFramework has been dirtied");
 | 
					                    .log_expect("InstallerFramework has been dirtied");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                let file = encapsulate_json(
 | 
					                let file = encapsulate_json(
 | 
				
			||||||
                    "config",
 | 
					                    "base_attributes",
 | 
				
			||||||
                    &framework
 | 
					                    &framework
 | 
				
			||||||
                        .get_config()
 | 
					                        .base_attributes
 | 
				
			||||||
                        .to_json_str()
 | 
					                        .to_json_str()
 | 
				
			||||||
                        .log_expect("Failed to render JSON representation of config"),
 | 
					                        .log_expect("Failed to render JSON representation of config"),
 | 
				
			||||||
                );
 | 
					                );
 | 
				
			||||||
| 
						 | 
					@ -98,6 +103,59 @@ impl Service for WebService {
 | 
				
			||||||
                    .with_header(ContentType::json())
 | 
					                    .with_header(ContentType::json())
 | 
				
			||||||
                    .with_body(file)
 | 
					                    .with_body(file)
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
 | 
					            // Returns the web config loaded
 | 
				
			||||||
 | 
					            (&Get, "/api/config") => {
 | 
				
			||||||
 | 
					                let mut framework = self
 | 
				
			||||||
 | 
					                    .framework
 | 
				
			||||||
 | 
					                    .write()
 | 
				
			||||||
 | 
					                    .log_expect("InstallerFramework has been dirtied");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                info!(
 | 
				
			||||||
 | 
					                    "Downloading configuration from {:?}...",
 | 
				
			||||||
 | 
					                    framework.base_attributes.target_url
 | 
				
			||||||
 | 
					                );
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                match http::download_text(&framework.base_attributes.target_url)
 | 
				
			||||||
 | 
					                    .map(|x| Config::from_toml_str(&x))
 | 
				
			||||||
 | 
					                {
 | 
				
			||||||
 | 
					                    Ok(Ok(config)) => {
 | 
				
			||||||
 | 
					                        framework.config = Some(config.clone());
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                        info!("Configuration file downloaded successfully.");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                        let file = framework
 | 
				
			||||||
 | 
					                            .get_config()
 | 
				
			||||||
 | 
					                            .log_expect("Config should be loaded by now")
 | 
				
			||||||
 | 
					                            .to_json_str()
 | 
				
			||||||
 | 
					                            .log_expect("Failed to render JSON representation of config");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                        Response::<hyper::Body>::new()
 | 
				
			||||||
 | 
					                            .with_header(ContentLength(file.len() as u64))
 | 
				
			||||||
 | 
					                            .with_header(ContentType::json())
 | 
				
			||||||
 | 
					                            .with_body(file)
 | 
				
			||||||
 | 
					                    }
 | 
				
			||||||
 | 
					                    Ok(Err(v)) => {
 | 
				
			||||||
 | 
					                        error!("Bad configuration file: {:?}", v);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                        Response::<hyper::Body>::new()
 | 
				
			||||||
 | 
					                            .with_status(StatusCode::ServiceUnavailable)
 | 
				
			||||||
 | 
					                            .with_header(ContentType::plaintext())
 | 
				
			||||||
 | 
					                            .with_body("Bad HTTP response")
 | 
				
			||||||
 | 
					                    }
 | 
				
			||||||
 | 
					                    Err(v) => {
 | 
				
			||||||
 | 
					                        error!(
 | 
				
			||||||
 | 
					                            "General connectivity error while downloading config: {:?}",
 | 
				
			||||||
 | 
					                            v
 | 
				
			||||||
 | 
					                        );
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                        Response::<hyper::Body>::new()
 | 
				
			||||||
 | 
					                            .with_status(StatusCode::ServiceUnavailable)
 | 
				
			||||||
 | 
					                            .with_header(ContentLength(v.len() as u64))
 | 
				
			||||||
 | 
					                            .with_header(ContentType::plaintext())
 | 
				
			||||||
 | 
					                            .with_body(v)
 | 
				
			||||||
 | 
					                    }
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
            // This endpoint should be usable directly from a <script> tag during loading.
 | 
					            // This endpoint should be usable directly from a <script> tag during loading.
 | 
				
			||||||
            (&Get, "/api/packages") => {
 | 
					            (&Get, "/api/packages") => {
 | 
				
			||||||
                let framework = self
 | 
					                let framework = self
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -40,7 +40,8 @@ impl ReleaseSource for GithubReleases {
 | 
				
			||||||
            .get(&format!(
 | 
					            .get(&format!(
 | 
				
			||||||
                "https://api.github.com/repos/{}/releases",
 | 
					                "https://api.github.com/repos/{}/releases",
 | 
				
			||||||
                config.repo
 | 
					                config.repo
 | 
				
			||||||
            )).header(UserAgent::new("liftinstall (j-selby)"))
 | 
					            ))
 | 
				
			||||||
 | 
					            .header(UserAgent::new("liftinstall (j-selby)"))
 | 
				
			||||||
            .send()
 | 
					            .send()
 | 
				
			||||||
            .map_err(|x| format!("Error while sending HTTP request: {:?}", x))?;
 | 
					            .map_err(|x| format!("Error while sending HTTP request: {:?}", x))?;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -52,8 +53,8 @@ impl ReleaseSource for GithubReleases {
 | 
				
			||||||
            .text()
 | 
					            .text()
 | 
				
			||||||
            .map_err(|x| format!("Failed to decode HTTP response body: {:?}", x))?;
 | 
					            .map_err(|x| format!("Failed to decode HTTP response body: {:?}", x))?;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        let result: serde_json::Value = serde_json::from_str(&body)
 | 
					        let result: serde_json::Value =
 | 
				
			||||||
            .map_err(|x| format!("Failed to parse response: {:?}", x))?;
 | 
					            serde_json::from_str(&body).map_err(|x| format!("Failed to parse response: {:?}", x))?;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        let result: &Vec<serde_json::Value> = result
 | 
					        let result: &Vec<serde_json::Value> = result
 | 
				
			||||||
            .as_array()
 | 
					            .as_array()
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -41,7 +41,12 @@ impl Task for InstallPackageTask {
 | 
				
			||||||
        let mut installed_files = Vec::new();
 | 
					        let mut installed_files = Vec::new();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        let mut metadata: Option<PackageDescription> = None;
 | 
					        let mut metadata: Option<PackageDescription> = None;
 | 
				
			||||||
        for description in &context.config.packages {
 | 
					        for description in &context
 | 
				
			||||||
 | 
					            .config
 | 
				
			||||||
 | 
					            .as_ref()
 | 
				
			||||||
 | 
					            .log_expect("Should have packages by now")
 | 
				
			||||||
 | 
					            .packages
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
            if self.name == description.name {
 | 
					            if self.name == description.name {
 | 
				
			||||||
                metadata = Some(description.clone());
 | 
					                metadata = Some(description.clone());
 | 
				
			||||||
                break;
 | 
					                break;
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -26,7 +26,12 @@ impl Task for ResolvePackageTask {
 | 
				
			||||||
    ) -> Result<TaskParamType, String> {
 | 
					    ) -> Result<TaskParamType, String> {
 | 
				
			||||||
        assert_eq!(input.len(), 0);
 | 
					        assert_eq!(input.len(), 0);
 | 
				
			||||||
        let mut metadata: Option<PackageDescription> = None;
 | 
					        let mut metadata: Option<PackageDescription> = None;
 | 
				
			||||||
        for description in &context.config.packages {
 | 
					        for description in &context
 | 
				
			||||||
 | 
					            .config
 | 
				
			||||||
 | 
					            .as_ref()
 | 
				
			||||||
 | 
					            .log_expect("Should have packages by now")
 | 
				
			||||||
 | 
					            .packages
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
            if self.name == description.name {
 | 
					            if self.name == description.name {
 | 
				
			||||||
                metadata = Some(description.clone());
 | 
					                metadata = Some(description.clone());
 | 
				
			||||||
                break;
 | 
					                break;
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -21,7 +21,7 @@
 | 
				
			||||||
                <div class="columns">
 | 
					                <div class="columns">
 | 
				
			||||||
                    <div class="column is-one-third" v-if="!is_launcher">
 | 
					                    <div class="column is-one-third" v-if="!is_launcher">
 | 
				
			||||||
                        <h1 class="title">
 | 
					                        <h1 class="title">
 | 
				
			||||||
                            Welcome to the {{ config.general.name }} installer!
 | 
					                            Welcome to the {{ attrs.name }} installer!
 | 
				
			||||||
                        </h1>
 | 
					                        </h1>
 | 
				
			||||||
                        <h2 class="subtitle">
 | 
					                        <h2 class="subtitle">
 | 
				
			||||||
                            We will have you up and running in just a few moments.
 | 
					                            We will have you up and running in just a few moments.
 | 
				
			||||||
| 
						 | 
					@ -37,6 +37,15 @@
 | 
				
			||||||
                        <a class="button is-primary is-pulled-right" v-on:click="back_to_packages">Back</a>
 | 
					                        <a class="button is-primary is-pulled-right" v-on:click="back_to_packages">Back</a>
 | 
				
			||||||
                    </div>
 | 
					                    </div>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    <div class="column" v-else-if="is_downloading_config">
 | 
				
			||||||
 | 
					                        <h4 class="subtitle">Downloading config...</h4>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                        <div v-html="progress_message"></div>
 | 
				
			||||||
 | 
					                        <progress class="progress is-info is-medium" v-bind:value="progress" max="100">
 | 
				
			||||||
 | 
					                            {{ progress }}%
 | 
				
			||||||
 | 
					                        </progress>
 | 
				
			||||||
 | 
					                    </div>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                    <div class="column" v-else-if="modify_install">
 | 
					                    <div class="column" v-else-if="modify_install">
 | 
				
			||||||
                        <h4 class="subtitle">Choose an option:</h4>
 | 
					                        <h4 class="subtitle">Choose an option:</h4>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -99,7 +108,7 @@
 | 
				
			||||||
                    <div class="column" v-else-if="is_installing">
 | 
					                    <div class="column" v-else-if="is_installing">
 | 
				
			||||||
                        <h4 class="subtitle" v-if="is_launcher">Checking for updates...</h4>
 | 
					                        <h4 class="subtitle" v-if="is_launcher">Checking for updates...</h4>
 | 
				
			||||||
                        <h4 class="subtitle" v-else>Installing...</h4>
 | 
					                        <h4 class="subtitle" v-else>Installing...</h4>
 | 
				
			||||||
                        <div v-html="config.general.installing_message"></div>
 | 
					                        <div v-html="config.installing_message"></div>
 | 
				
			||||||
                        <br />
 | 
					                        <br />
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                        <div v-html="progress_message"></div>
 | 
					                        <div v-html="progress_message"></div>
 | 
				
			||||||
| 
						 | 
					@ -109,7 +118,7 @@
 | 
				
			||||||
                    </div>
 | 
					                    </div>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                    <div class="column" v-else-if="is_finished">
 | 
					                    <div class="column" v-else-if="is_finished">
 | 
				
			||||||
                        <h4 class="subtitle">Thanks for installing {{ config.general.name }}!</h4>
 | 
					                        <h4 class="subtitle">Thanks for installing {{ attrs.name }}!</h4>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                        <a class="button is-primary is-pulled-right" v-on:click="exit">Exit</a>
 | 
					                        <a class="button is-primary is-pulled-right" v-on:click="exit">Exit</a>
 | 
				
			||||||
                    </div>
 | 
					                    </div>
 | 
				
			||||||
| 
						 | 
					@ -126,7 +135,7 @@
 | 
				
			||||||
            <div class="modal-background"></div>
 | 
					            <div class="modal-background"></div>
 | 
				
			||||||
            <div class="modal-card">
 | 
					            <div class="modal-card">
 | 
				
			||||||
                <header class="modal-card-head">
 | 
					                <header class="modal-card-head">
 | 
				
			||||||
                    <p class="modal-card-title">Are you sure you want to uninstall {{ config.general.name }}?</p>
 | 
					                    <p class="modal-card-title">Are you sure you want to uninstall {{ attrs.name }}?</p>
 | 
				
			||||||
                </header>
 | 
					                </header>
 | 
				
			||||||
                <footer class="modal-card-foot">
 | 
					                <footer class="modal-card-foot">
 | 
				
			||||||
                    <button class="button is-danger" v-on:click="uninstall">Yes</button>
 | 
					                    <button class="button is-danger" v-on:click="uninstall">Yes</button>
 | 
				
			||||||
| 
						 | 
					@ -136,7 +145,7 @@
 | 
				
			||||||
        </div>
 | 
					        </div>
 | 
				
			||||||
    </div>
 | 
					    </div>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    <script src="/api/config"></script>
 | 
					    <script src="/api/attrs"></script>
 | 
				
			||||||
    <script src="/js/helpers.js"></script>
 | 
					    <script src="/js/helpers.js"></script>
 | 
				
			||||||
    <script src="/js/vue.min.js"></script>
 | 
					    <script src="/js/vue.min.js"></script>
 | 
				
			||||||
    <script>
 | 
					    <script>
 | 
				
			||||||
| 
						 | 
					@ -168,7 +177,7 @@
 | 
				
			||||||
            intercept(methods[i]);
 | 
					            intercept(methods[i]);
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        document.getElementById("window-title").innerText = config.name + " Installer";
 | 
					        document.getElementById("window-title").innerText = base_attributes.name + " Installer";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        function selectFileCallback(name) {
 | 
					        function selectFileCallback(name) {
 | 
				
			||||||
            app.install_location = name;
 | 
					            app.install_location = name;
 | 
				
			||||||
| 
						 | 
					@ -177,7 +186,8 @@
 | 
				
			||||||
        var app = new Vue({
 | 
					        var app = new Vue({
 | 
				
			||||||
            el: '#app',
 | 
					            el: '#app',
 | 
				
			||||||
            data: {
 | 
					            data: {
 | 
				
			||||||
                config : config,
 | 
					                attrs: base_attributes,
 | 
				
			||||||
 | 
					                config : {},
 | 
				
			||||||
                install_location : "",
 | 
					                install_location : "",
 | 
				
			||||||
                // If the initial modify menu should be shown
 | 
					                // If the initial modify menu should be shown
 | 
				
			||||||
                modify_install : false,
 | 
					                modify_install : false,
 | 
				
			||||||
| 
						 | 
					@ -192,6 +202,8 @@
 | 
				
			||||||
                launcher_path : undefined,
 | 
					                launcher_path : undefined,
 | 
				
			||||||
                // If a confirmation prompt should be shown
 | 
					                // If a confirmation prompt should be shown
 | 
				
			||||||
                confirm_uninstall : false,
 | 
					                confirm_uninstall : false,
 | 
				
			||||||
 | 
					                // If the downloading config page should be shown
 | 
				
			||||||
 | 
					                is_downloading_config : false,
 | 
				
			||||||
                progress : 0,
 | 
					                progress : 0,
 | 
				
			||||||
                progress_message : "",
 | 
					                progress_message : "",
 | 
				
			||||||
                has_error : false,
 | 
					                has_error : false,
 | 
				
			||||||
| 
						 | 
					@ -205,6 +217,59 @@
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
            },
 | 
					            },
 | 
				
			||||||
            methods: {
 | 
					            methods: {
 | 
				
			||||||
 | 
					                "download_config": function() {
 | 
				
			||||||
 | 
					                    app.is_downloading_config = true;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    ajax("/api/config", function(e) {
 | 
				
			||||||
 | 
					                        app.download_install_status();
 | 
				
			||||||
 | 
					                        app.config = e;
 | 
				
			||||||
 | 
					                    });
 | 
				
			||||||
 | 
					                },
 | 
				
			||||||
 | 
					                "download_install_status": function() {
 | 
				
			||||||
 | 
					                    ajax("/api/installation-status", function(e) {
 | 
				
			||||||
 | 
					                        app.is_downloading_config = false;
 | 
				
			||||||
 | 
					                        app.metadata = e;
 | 
				
			||||||
 | 
					                        if (e.preexisting_install) {
 | 
				
			||||||
 | 
					                            app.modify_install = true;
 | 
				
			||||||
 | 
					                            app.select_packages = false;
 | 
				
			||||||
 | 
					                            app.show_install_location = false;
 | 
				
			||||||
 | 
					                            app.install_location = e.install_path;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                            // Copy over installed packages
 | 
				
			||||||
 | 
					                            for (var 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.length; i++) {
 | 
				
			||||||
 | 
					                                // Find this config package
 | 
				
			||||||
 | 
					                                for (var x = 0; x < app.config.packages.length; x++) {
 | 
				
			||||||
 | 
					                                    if (app.config.packages[x].name === app.metadata.database[i].name) {
 | 
				
			||||||
 | 
					                                        app.config.packages[x].default = true;
 | 
				
			||||||
 | 
					                                        app.config.packages[x].installed = true;
 | 
				
			||||||
 | 
					                                    }
 | 
				
			||||||
 | 
					                                }
 | 
				
			||||||
 | 
					                            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                            if (e.is_launcher) {
 | 
				
			||||||
 | 
					                                document.getElementById("window-title").innerText = config.name + " Updater";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                                app.is_launcher = true;
 | 
				
			||||||
 | 
					                                app.install();
 | 
				
			||||||
 | 
					                            }
 | 
				
			||||||
 | 
					                        } else {
 | 
				
			||||||
 | 
					                            for (var x = 0; x < app.config.packages.length; x++) {
 | 
				
			||||||
 | 
					                                app.config.packages[x].installed = false;
 | 
				
			||||||
 | 
					                            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                            ajax("/api/default-path", function(e) {
 | 
				
			||||||
 | 
					                                if (e.path != null) {
 | 
				
			||||||
 | 
					                                    app.install_location = e.path;
 | 
				
			||||||
 | 
					                                }
 | 
				
			||||||
 | 
					                            });
 | 
				
			||||||
 | 
					                        }
 | 
				
			||||||
 | 
					                    });
 | 
				
			||||||
 | 
					                },
 | 
				
			||||||
                "select_file": function() {
 | 
					                "select_file": function() {
 | 
				
			||||||
                    window.external.invoke(JSON.stringify({
 | 
					                    window.external.invoke(JSON.stringify({
 | 
				
			||||||
                        SelectInstallDir: {
 | 
					                        SelectInstallDir: {
 | 
				
			||||||
| 
						 | 
					@ -297,48 +362,7 @@
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
        });
 | 
					        });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        ajax("/api/installation-status", function(e) {
 | 
					        app.download_config();
 | 
				
			||||||
            app.metadata = e;
 | 
					 | 
				
			||||||
            if (e.preexisting_install) {
 | 
					 | 
				
			||||||
                app.modify_install = true;
 | 
					 | 
				
			||||||
                app.select_packages = false;
 | 
					 | 
				
			||||||
                app.show_install_location = false;
 | 
					 | 
				
			||||||
                app.install_location = e.install_path;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
                // Copy over installed packages
 | 
					 | 
				
			||||||
                for (var 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.length; i++) {
 | 
					 | 
				
			||||||
                    // Find this config package
 | 
					 | 
				
			||||||
                    for (var x = 0; x < app.config.packages.length; x++) {
 | 
					 | 
				
			||||||
                        if (app.config.packages[x].name === app.metadata.database[i].name) {
 | 
					 | 
				
			||||||
                            app.config.packages[x].default = true;
 | 
					 | 
				
			||||||
                            app.config.packages[x].installed = true;
 | 
					 | 
				
			||||||
                        }
 | 
					 | 
				
			||||||
                    }
 | 
					 | 
				
			||||||
                }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
                if (e.is_launcher) {
 | 
					 | 
				
			||||||
                    document.getElementById("window-title").innerText = config.name + " Updater";
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
                    app.is_launcher = true;
 | 
					 | 
				
			||||||
                    app.install();
 | 
					 | 
				
			||||||
                }
 | 
					 | 
				
			||||||
            } else {
 | 
					 | 
				
			||||||
                for (var x = 0; x < app.config.packages.length; x++) {
 | 
					 | 
				
			||||||
                    app.config.packages[x].installed = false;
 | 
					 | 
				
			||||||
                }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
                ajax("/api/default-path", function(e) {
 | 
					 | 
				
			||||||
                    if (e.path != null) {
 | 
					 | 
				
			||||||
                        app.install_location = e.path;
 | 
					 | 
				
			||||||
                    }
 | 
					 | 
				
			||||||
                });
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
        });
 | 
					 | 
				
			||||||
    </script>
 | 
					    </script>
 | 
				
			||||||
</body>
 | 
					</body>
 | 
				
			||||||
</html>
 | 
					</html>
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
		Loading…
	
		Reference in a new issue