Do that routing magic

This commit is contained in:
James 2018-08-07 20:17:01 +10:00
parent 0634e1a328
commit cada46738a
10 changed files with 256 additions and 73 deletions

20
Cargo.lock generated
View File

@ -369,6 +369,7 @@ dependencies = [
"serde_json 1.0.9 (registry+https://github.com/rust-lang/crates.io-index)", "serde_json 1.0.9 (registry+https://github.com/rust-lang/crates.io-index)",
"toml 0.4.5 (registry+https://github.com/rust-lang/crates.io-index)", "toml 0.4.5 (registry+https://github.com/rust-lang/crates.io-index)",
"url 1.6.0 (registry+https://github.com/rust-lang/crates.io-index)", "url 1.6.0 (registry+https://github.com/rust-lang/crates.io-index)",
"walkdir 2.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
"web-view 0.2.1 (git+https://github.com/Boscop/web-view.git?rev=555f422d09cbb94e82a728d47e9e07ca91963f6e)", "web-view 0.2.1 (git+https://github.com/Boscop/web-view.git?rev=555f422d09cbb94e82a728d47e9e07ca91963f6e)",
"winres 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", "winres 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)",
"zip 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", "zip 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)",
@ -732,6 +733,14 @@ name = "safemem"
version = "0.2.0" version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "same-file"
version = "1.0.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"winapi 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]] [[package]]
name = "schannel" name = "schannel"
version = "0.1.10" version = "0.1.10"
@ -1089,6 +1098,15 @@ name = "void"
version = "1.0.2" version = "1.0.2"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "walkdir"
version = "2.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"same-file 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)",
"winapi 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]] [[package]]
name = "want" name = "want"
version = "0.0.4" version = "0.0.4"
@ -1263,6 +1281,7 @@ dependencies = [
"checksum relay 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "f301bafeb60867c85170031bdb2fcf24c8041f33aee09e7b116a58d4e9f781c5" "checksum relay 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "f301bafeb60867c85170031bdb2fcf24c8041f33aee09e7b116a58d4e9f781c5"
"checksum reqwest 0.8.6 (registry+https://github.com/rust-lang/crates.io-index)" = "2abe46f8e00792693a2488e296c593d1f4ea39bb1178cfce081d6793657575e4" "checksum reqwest 0.8.6 (registry+https://github.com/rust-lang/crates.io-index)" = "2abe46f8e00792693a2488e296c593d1f4ea39bb1178cfce081d6793657575e4"
"checksum safemem 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "e27a8b19b835f7aea908818e871f5cc3a5a186550c30773be987e155e8163d8f" "checksum safemem 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "e27a8b19b835f7aea908818e871f5cc3a5a186550c30773be987e155e8163d8f"
"checksum same-file 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "cfb6eded0b06a0b512c8ddbcf04089138c9b4362c2f696f3c3d76039d68f3637"
"checksum schannel 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)" = "acece75e0f987c48863a6c792ec8b7d6c4177d4a027f8ccc72f849794f437016" "checksum schannel 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)" = "acece75e0f987c48863a6c792ec8b7d6c4177d4a027f8ccc72f849794f437016"
"checksum scoped-tls 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "f417c22df063e9450888a7561788e9bd46d3bb3c1466435b4eccb903807f147d" "checksum scoped-tls 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "f417c22df063e9450888a7561788e9bd46d3bb3c1466435b4eccb903807f147d"
"checksum security-framework 0.1.16 (registry+https://github.com/rust-lang/crates.io-index)" = "dfa44ee9c54ce5eecc9de7d5acbad112ee58755239381f687e564004ba4a2332" "checksum security-framework 0.1.16 (registry+https://github.com/rust-lang/crates.io-index)" = "dfa44ee9c54ce5eecc9de7d5acbad112ee58755239381f687e564004ba4a2332"
@ -1309,6 +1328,7 @@ dependencies = [
"checksum vec_map 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)" = "05c78687fb1a80548ae3250346c3db86a80a7cdd77bda190189f2d0a0987c81a" "checksum vec_map 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)" = "05c78687fb1a80548ae3250346c3db86a80a7cdd77bda190189f2d0a0987c81a"
"checksum version_check 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "6b772017e347561807c1aa192438c5fd74242a670a6cffacc40f2defd1dc069d" "checksum version_check 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "6b772017e347561807c1aa192438c5fd74242a670a6cffacc40f2defd1dc069d"
"checksum void 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "6a02e4885ed3bc0f2de90ea6dd45ebcbb66dacffe03547fadbb0eeae2770887d" "checksum void 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "6a02e4885ed3bc0f2de90ea6dd45ebcbb66dacffe03547fadbb0eeae2770887d"
"checksum walkdir 2.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "f1b768ba943161a9226ccd59b26bcd901e5d60e6061f4fcad3034784e0c7372b"
"checksum want 0.0.4 (registry+https://github.com/rust-lang/crates.io-index)" = "a05d9d966753fa4b5c8db73fcab5eed4549cfe0e1e4e66911e5564a0085c35d1" "checksum want 0.0.4 (registry+https://github.com/rust-lang/crates.io-index)" = "a05d9d966753fa4b5c8db73fcab5eed4549cfe0e1e4e66911e5564a0085c35d1"
"checksum web-view 0.2.1 (git+https://github.com/Boscop/web-view.git?rev=555f422d09cbb94e82a728d47e9e07ca91963f6e)" = "<none>" "checksum web-view 0.2.1 (git+https://github.com/Boscop/web-view.git?rev=555f422d09cbb94e82a728d47e9e07ca91963f6e)" = "<none>"
"checksum webview-sys 0.1.0 (git+https://github.com/Boscop/web-view.git?rev=555f422d09cbb94e82a728d47e9e07ca91963f6e)" = "<none>" "checksum webview-sys 0.1.0 (git+https://github.com/Boscop/web-view.git?rev=555f422d09cbb94e82a728d47e9e07ca91963f6e)" = "<none>"

View File

@ -40,6 +40,9 @@ chrono = "0.4.5"
clap = "2.32.0" clap = "2.32.0"
[build-dependencies]
walkdir = "2"
[target.'cfg(windows)'.dependencies] [target.'cfg(windows)'.dependencies]
# NFD is needed on Windows, as web-view doesn't work correctly here # NFD is needed on Windows, as web-view doesn't work correctly here
nfd = "0.0.4" nfd = "0.0.4"

103
build.rs
View File

@ -1,12 +1,111 @@
extern crate walkdir;
#[cfg(windows)] #[cfg(windows)]
extern crate winres; extern crate winres;
use walkdir::WalkDir;
use std::env;
use std::path::PathBuf;
use std::fs::copy;
use std::fs::create_dir_all;
use std::fs::File;
use std::io::BufRead;
use std::io::BufReader;
use std::io::Write;
const FILES_TO_PREPROCESS: &'static [&'static str] = &["helpers.js", "views.js"];
#[cfg(windows)] #[cfg(windows)]
fn main() { fn handle_binary() {
let mut res = winres::WindowsResource::new(); let mut res = winres::WindowsResource::new();
res.set_icon("static/favicon.ico"); res.set_icon("static/favicon.ico");
res.compile().expect("Failed to generate metadata"); res.compile().expect("Failed to generate metadata");
} }
#[cfg(not(windows))] #[cfg(not(windows))]
fn main() {} fn handle_binary() {}
fn main() {
handle_binary();
let output_dir = PathBuf::from(env::var("OUT_DIR").unwrap());
// Copy files from static/ to build dir
for entry in WalkDir::new("static") {
let entry = entry.expect("Unable to read output directory");
let output_file = output_dir.join(entry.path());
if entry.path().is_dir() {
create_dir_all(output_file).expect("Unable to create dir");
} else {
let filename = entry
.path()
.file_name()
.expect("Unable to parse filename")
.to_str()
.expect("Unable to convert to string");
if FILES_TO_PREPROCESS.contains(&filename) {
// Do basic preprocessing - transcribe template string
let source = BufReader::new(File::open(entry.path()).expect("Unable to copy file"));
let mut target = File::create(output_file).expect("Unable to copy file");
let mut is_template_string = false;
for line in source.lines() {
let line = line.expect("Unable to read line from JS file");
let mut is_break = false;
let mut is_quote = false;
let mut output_line = String::new();
if is_template_string {
output_line += "\"";
}
for c in line.chars() {
if c == '\\' {
is_break = true;
output_line.push('\\');
continue;
}
if (c == '\"' || c == '\'') && !is_break && !is_template_string {
is_quote = !is_quote;
}
if c == '`' && !is_break && !is_quote {
output_line += "\"";
is_template_string = !is_template_string;
continue;
}
if c == '"' && !is_break && is_template_string {
output_line += "\\\"";
continue;
}
is_break = false;
output_line.push(c);
}
if is_template_string {
output_line += "\" +";
}
output_line.push('\n');
target
.write(output_line.as_bytes())
.expect("Unable to write line");
}
} else {
copy(entry.path(), output_file).expect("Unable to copy file");
}
}
}
}

View File

@ -8,7 +8,7 @@ macro_rules! include_files_as_assets {
( $target_match:expr, $( $file_name:expr ),* ) => { ( $target_match:expr, $( $file_name:expr ),* ) => {
match $target_match { match $target_match {
$( $(
$file_name => Some(include_bytes!(concat!("../static/", $file_name)).as_ref()), $file_name => Some(include_bytes!(concat!(concat!(env!("OUT_DIR"), "/static/"), $file_name)).as_ref()),
)* )*
_ => None _ => None
} }

View File

@ -17,8 +17,7 @@ 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()?;

View File

@ -96,8 +96,7 @@ 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);

View File

@ -59,8 +59,7 @@ 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");
}); });

View File

@ -40,8 +40,7 @@ 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))?;
@ -53,8 +52,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 = let result: serde_json::Value = serde_json::from_str(&body)
serde_json::from_str(&body).map_err(|x| format!("Failed to parse response: {:?}", x))?; .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()

View File

@ -141,7 +141,7 @@
</div> </div>
</section> </section>
<div class="modal is-active" v-if="confirm_uninstall"> <!--<div class="modal is-active" v-if="confirm_uninstall">
<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">
@ -152,7 +152,7 @@
<button class="button" v-on:click="cancel_uninstall">No</button> <button class="button" v-on:click="cancel_uninstall">No</button>
</footer> </footer>
</div> </div>
</div> </div>-->
</div> </div>
<script src="/js/vue.js"></script> <script src="/js/vue.js"></script>
@ -196,32 +196,32 @@
} }
var app = new Vue({ var app = new Vue({
router, router: router,
data: { data: {
attrs: base_attributes, attrs: base_attributes,
config : {}, 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,
// If the package selection screen should be shown // If the package selection screen should be shown
select_packages : true, select_packages : true,
// If an installation operation is happening // If an installation operation is happening
is_installing : false, is_installing : false,
// If an installation has completed successfully // If an installation has completed successfully
is_finished : false, is_finished : false,*/
// If the application should act as an launcher, rather than as an installer // If the application should act as an launcher, rather than as an installer
is_launcher : false, is_launcher : false,
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 // If the downloading config page should be shown
is_downloading_config : false, is_downloading_config : false,
progress : 0, progress : 0,
progress_message : "", progress_message : "",
has_error : false, has_error : false,*/
// If the option to pick an install location should be provided // If the option to pick an install location should be provided
show_install_location : true, show_install_location : true,
error : "", //error : "",
metadata : { metadata : {
database : [], database : [],
install_path : "", install_path : "",
@ -229,22 +229,6 @@
} }
}, },
methods: { methods: {
"back_to_packages": function() {
app.select_packages = true;
app.has_error = false;
app.is_installing = false;
app.is_finished = false;
},
"prepare_uninstall": function() {
app.confirm_uninstall = true;
},
"cancel_uninstall": function() {
app.confirm_uninstall = false;
},
"modify_packages": function() {
app.select_packages = true;
app.modify_install = false;
},
"exit": function() { "exit": function() {
ajax("/api/exit", function() {}); ajax("/api/exit", function() {});
} }

View File

@ -1,6 +1,6 @@
const DownloadConfig = { const DownloadConfig = {
template: ` template: `
<div class="column"> <div class="column has-padding">
<h4 class="subtitle">Downloading config...</h4> <h4 class="subtitle">Downloading config...</h4>
<br /> <br />
@ -14,19 +14,23 @@ const DownloadConfig = {
}, },
methods: { methods: {
download_install_status: function() { download_install_status: function() {
ajax("/api/installation-status", (e) => { var that = this; // IE workaround
ajax("/api/installation-status", function(e) {
app.metadata = e; app.metadata = e;
this.download_config(); that.download_config();
}); });
}, },
download_config: function() { download_config: function() {
ajax("/api/config", (e) => { var that = this; // IE workaround
ajax("/api/config", function(e) {
app.config = e; app.config = e;
this.choose_next_state(); that.choose_next_state();
}, (e) => { }, function(e) {
console.error("Got error while downloading config: " console.error("Got error while downloading config: "
+ e); + e);
@ -71,7 +75,7 @@ const DownloadConfig = {
// Need to do a bit more digging to get at the // Need to do a bit more digging to get at the
// install location. // install location.
ajax("/api/default-path", (e) => { ajax("/api/default-path", function(e) {
if (e.path != null) { if (e.path != null) {
app.install_location = e.path; app.install_location = e.path;
} }
@ -79,30 +83,13 @@ const DownloadConfig = {
router.replace("/packages"); router.replace("/packages");
} }
/*app.is_downloading_config = false;
if (e.preexisting_install) {
app.modify_install = true;
app.select_packages = false;
app.show_install_location = false;
app.install_location = e.install_path;
if (e.is_launcher) {
app.is_launcher = true;
app.install();
}
} else {
}*/
} }
} }
}; };
const SelectPackages = { const SelectPackages = {
template: ` template: `
<div class="column"> <div class="column has-padding">
<h4 class="subtitle">Select your preferred settings:</h4> <h4 class="subtitle">Select your preferred settings:</h4>
<!-- Build options --> <!-- Build options -->
@ -130,13 +117,18 @@ const SelectPackages = {
placeholder="Enter a install path here"> placeholder="Enter a install path here">
</div> </div>
<div class="control"> <div class="control">
<a class="button is-info" v-on:click="select_file"> <a class="button is-dark" v-on:click="select_file">
Select Select
</a> </a>
</div> </div>
</div> </div>
<a class="button is-primary is-pulled-right" v-on:click="install">Install!</a> <a class="button is-dark is-pulled-right" v-if="!$root.$data.metadata.preexisting_install"
v-on:click="install">Install!</a>
<a class="button is-dark is-pulled-right" v-if="$root.$data.metadata.preexisting_install"
v-on:click="install">Modify</a>
<a class="button is-pulled-left" v-if="$root.$data.metadata.preexisting_install"
v-on:click="go_back">Back</a>
</div> </div>
`, `,
methods: { methods: {
@ -149,13 +141,16 @@ const SelectPackages = {
}, },
install: function() { install: function() {
router.push("/install/regular"); router.push("/install/regular");
},
go_back: function() {
router.go(-1);
} }
} }
}; };
const InstallPackages = { const InstallPackages = {
template: ` template: `
<div class="column"> <div class="column has-padding">
<h4 class="subtitle" v-if="$root.$data.metadata.is_launcher">Checking for updates...</h4> <h4 class="subtitle" v-if="$root.$data.metadata.is_launcher">Checking for updates...</h4>
<h4 class="subtitle" v-else-if="is_uninstall">Uninstalling...</h4> <h4 class="subtitle" v-else-if="is_uninstall">Uninstalling...</h4>
<h4 class="subtitle" v-else>Installing...</h4> <h4 class="subtitle" v-else>Installing...</h4>
@ -193,25 +188,27 @@ const InstallPackages = {
results["path"] = app.install_location; results["path"] = app.install_location;
var that = this; // IE workaround
stream_ajax(this.is_uninstall ? "/api/uninstall" : stream_ajax(this.is_uninstall ? "/api/uninstall" :
"/api/start-install", (line) => { "/api/start-install", function(line) {
if (line.hasOwnProperty("Status")) { if (line.hasOwnProperty("Status")) {
this.progress_message = line.Status[0]; that.progress_message = line.Status[0];
this.progress = line.Status[1] * 100; that.progress = line.Status[1] * 100;
} }
if (line.hasOwnProperty("Error")) { if (line.hasOwnProperty("Error")) {
if (app.metadata.is_launcher) { if (app.metadata.is_launcher) {
app.exit(); app.exit();
} else { } else {
this.failed_with_error = true; that.failed_with_error = true;
router.replace({name: 'showerr', params: {msg: line.Error}}); router.replace({name: 'showerr', params: {msg: line.Error}});
} }
} }
}, (e) => { }, function(e) {
if (app.metadata.is_launcher) { if (app.metadata.is_launcher) {
app.exit(); app.exit();
} else if (!this.failed_with_error) { } else if (!that.failed_with_error) {
router.push("/complete"); router.push("/complete");
} }
}, undefined, results); }, undefined, results);
@ -221,7 +218,7 @@ const InstallPackages = {
const ErrorView = { const ErrorView = {
template: ` template: `
<div class="column"> <div class="column has-padding">
<h4 class="subtitle">An error occurred:</h4> <h4 class="subtitle">An error occurred:</h4>
<code>{{ msg }}</code> <code>{{ msg }}</code>
@ -242,7 +239,81 @@ const ErrorView = {
} }
}; };
const const CompleteView = {
template: `
<div class="column has-padding">
<h4 class="subtitle">Thanks for installing {{ $root.$data.attrs.name }}!</h4>
<a class="button is-dark is-pulled-right" v-on:click="exit">Exit</a>
</div>
`,
methods: {
exit: function() {
app.exit();
}
}
};
const ModifyView = {
template: `
<div class="column has-padding">
<h4 class="subtitle">Choose an option:</h4>
<div class="field is-grouped">
<p class="control">
<a class="button is-link" v-on:click="update">
Update
</a>
</p>
<p class="control">
<a class="button" v-on:click="modify_packages">
Modify
</a>
</p>
<p class="control">
<a class="button is-danger" v-on:click="prepare_uninstall">
Uninstall
</a>
</p>
</div>
<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>
</div>
`,
data: function() {
return {
show_uninstall: false
}
},
methods: {
update: function() {
router.push("/install/regular");
},
modify_packages: function() {
router.push("/packages");
},
prepare_uninstall: function() {
this.show_uninstall = true;
},
cancel_uninstall: function() {
this.show_uninstall = false;
},
uninstall: function() {
router.push("/install/uninstall");
},
}
};
const router = new VueRouter({ const router = new VueRouter({
routes: [ routes: [
@ -266,6 +337,16 @@ const router = new VueRouter({
name: 'showerr', name: 'showerr',
component: ErrorView component: ErrorView
}, },
{
path: '/complete',
name: 'complete',
component: CompleteView
},
{
path: '/modify',
name: 'modify',
component: ModifyView
},
{ {
path: '/', path: '/',
redirect: '/config' redirect: '/config'