ux: allow user to overwrite the directory...

... if they really want
This commit is contained in:
liushuyu 2020-05-28 00:36:05 -06:00
parent 45c562d723
commit 928661db77
6 changed files with 100 additions and 24 deletions

View File

@ -31,6 +31,7 @@ mod packages;
mod static_files; mod static_files;
mod uninstall; mod uninstall;
mod update_updater; mod update_updater;
mod verify_path;
/// Expected incoming Request format from Hyper. /// Expected incoming Request format from Hyper.
pub type Request = hyper::server::Request; pub type Request = hyper::server::Request;
@ -140,6 +141,7 @@ impl Service for WebService {
(Method::Post, "/api/start-install") => install::handle(self, req), (Method::Post, "/api/start-install") => install::handle(self, req),
(Method::Post, "/api/uninstall") => uninstall::handle(self, req), (Method::Post, "/api/uninstall") => uninstall::handle(self, req),
(Method::Post, "/api/update-updater") => update_updater::handle(self, req), (Method::Post, "/api/update-updater") => update_updater::handle(self, req),
(Method::Post, "/api/verify-path") => verify_path::handle(self, req),
(Method::Get, _) => static_files::handle(self, req), (Method::Get, _) => static_files::handle(self, req),
e => { e => {
info!("Returned 404 for {:?}", e); info!("Returned 404 for {:?}", e);

View 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)
}))
}

View File

@ -25,7 +25,13 @@ impl Task for RemoveTargetDirTask {
if let Some(path) = context.install_path.as_ref() { if let Some(path) = context.install_path.as_ref() {
let entries = std::fs::read_dir(path) let entries = std::fs::read_dir(path)
.map_err(|e| format!("Error reading {}: {}", path.to_string_lossy(), e))?; .map_err(|e| format!("Error reading {}: {}", path.to_string_lossy(), e))?;
// remove everything except the maintenancetool // 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 { for entry in entries {
let path = entry let path = entry
.map_err(|e| format!("Error reading file: {}", e))? .map_err(|e| format!("Error reading file: {}", e))?

View File

@ -48,7 +48,9 @@
"modify":"Modify", "modify":"Modify",
"repair": "Repair", "repair": "Repair",
"uninstall":"Uninstall", "uninstall":"Uninstall",
"prompt":"Are you sure you want to uninstall {name}?" "view_local_files": "View local files",
"prompt":"Are you sure you want to uninstall {name}?",
"prompt_confirm":"Uninstall {name}"
}, },
"back":"Back", "back":"Back",
"exit":"Exit", "exit":"Exit",

View File

@ -20,22 +20,15 @@
<br /> <br />
<br /> <br />
<b-button class="is-dark is-medium" v-on:click="prepare_uninstall"> <a class="button is-dark is-medium" v-on:click="prepare_uninstall">
{{ $t('modify.uninstall') }} {{ $t('modify.uninstall') }}
</b-button> </a>
<br />
<br />
<div class="modal is-active" v-if="show_uninstall"> <a class="button is-dark is-medium" v-on:click="view_files">
<div class="modal-background"></div> {{ $t('modify.view_local_files') }}
<div class="modal-card"> </a>
<header class="modal-card-head">
<p class="modal-card-title">{{ $t('modify.prompt', {'name': $root.$data.attrs.name}) }}</p>
</header>
<footer class="modal-card-foot">
<button class="button is-danger" v-on:click="uninstall">{{ $t('yes') }}</button>
<button class="button" v-on:click="cancel_uninstall">{{ $t('no') }}</button>
</footer>
</div>
</div>
</div> </div>
</template> </template>
@ -43,9 +36,7 @@
export default { export default {
name: 'ModifyView', name: 'ModifyView',
data: function () { data: function () {
return { return {}
show_uninstall: false
}
}, },
methods: { methods: {
update: function () { update: function () {
@ -58,10 +49,14 @@ export default {
this.$router.push({ name: 'packages', params: { repair: true } }) this.$router.push({ name: 'packages', params: { repair: true } })
}, },
prepare_uninstall: function () { prepare_uninstall: function () {
this.show_uninstall = true this.$buefy.dialog.confirm({
}, title: this.$t('modify.uninstall'),
cancel_uninstall: function () { message: this.$t('modify.prompt', {'name': this.$root.$data.attrs.name}),
this.show_uninstall = false confirmText: this.$t('modify.prompt_confirm', {'name': this.$root.$data.attrs.name}),
type: 'is-danger',
hasIcon: true,
onConfirm: this.uninstall
})
}, },
uninstall: function () { uninstall: function () {
this.$router.push('/install/uninstall') this.$router.push('/install/uninstall')

View File

@ -81,8 +81,30 @@ export default {
} }
})) }))
}, },
show_overwrite_dialog: function (confirmCallback) {
this.$buefy.dialog.confirm({
title: 'Overwriting',
message: `Directory ${this.$root.$data.install_location} already exists.<br>
Are you sure you want to <b>overwrite</b> the contents inside?`,
confirmText: 'Continue',
type: 'is-danger',
hasIcon: true,
onConfirm: confirmCallback
})
},
install: function () { install: function () {
this.$router.push(this.repair ? '/install/repair' : '/install/regular') this.repair && this.$router.push('/install/repair')
var my = this
this.$http.post('/api/verify-path', `path=${this.$root.$data.install_location}`).then(function (resp) {
var 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 () { go_back: function () {
this.$router.go(-1) this.$router.go(-1)