public inbox for pve-devel@lists.proxmox.com
 help / color / mirror / Atom feed
* [pve-devel] [PATCH-SERIES v4 http-server/storage/manager] fix #3505: add checksum and algorithm to iso upload
@ 2021-08-31 10:16 Lorenz Stechauner
  2021-08-31 10:16 ` [pve-devel] [PATCH v4 http-server 1/1] anyevent: move unlink from http-server to endpoint Lorenz Stechauner
                   ` (9 more replies)
  0 siblings, 10 replies; 17+ messages in thread
From: Lorenz Stechauner @ 2021-08-31 10:16 UTC (permalink / raw)
  To: pve-devel

changes to v3:
* added comment regarding tmpfile cleanup
* added commit fixing the cleanup of tmpfile
* fixed commit in http-server; every call to cleanup_reqchange() 
    is now updated


Lorenz Stechauner (1):
  anyevent: move unlink from http-server to endpoint

 src/PVE/APIServer/AnyEvent.pm | 8 ++++----
 1 file changed, 4 insertions(+), 4 deletions(-)


Lorenz Stechauner (4):
  status: move unlink from http-server to enpoint
  status: remove sleep(1) in file upload
  fix #3505: status: add checksum and algorithm to file upload
  status: fix tmpfile cleanup

 PVE/API2/Storage/Status.pm | 56 +++++++++++++++++++++++++++++++-------
 1 file changed, 46 insertions(+), 10 deletions(-)


Lorenz Stechauner (5):
  ui: move upload window into UploadToStorage.js
  ui: refactor UploadToStorage.js
  fix #3505: ui/UploadToStorage: add checksum and algorithm
  ui/UploadToStorage: add TaskViewer
  ui/UplaodToStorage: check file extension

 www/manager6/Makefile                  |   1 +
 www/manager6/storage/ContentView.js    | 197 +----------------
 www/manager6/window/UploadToStorage.js | 295 +++++++++++++++++++++++++
 3 files changed, 298 insertions(+), 195 deletions(-)
 create mode 100644 www/manager6/window/UploadToStorage.js


-- 
2.30.2





^ permalink raw reply	[flat|nested] 17+ messages in thread

* [pve-devel] [PATCH v4 http-server 1/1] anyevent: move unlink from http-server to endpoint
  2021-08-31 10:16 [pve-devel] [PATCH-SERIES v4 http-server/storage/manager] fix #3505: add checksum and algorithm to iso upload Lorenz Stechauner
@ 2021-08-31 10:16 ` Lorenz Stechauner
  2021-10-04  8:16   ` [pve-devel] applied: " Thomas Lamprecht
  2021-08-31 10:16 ` [pve-devel] [PATCH v4 storage 1/4] status: move unlink from http-server to enpoint Lorenz Stechauner
                   ` (8 subsequent siblings)
  9 siblings, 1 reply; 17+ messages in thread
From: Lorenz Stechauner @ 2021-08-31 10:16 UTC (permalink / raw)
  To: pve-devel

any uploaded file has to be deleted by the corrosponding
endpoint. the file upload was only used by the 'upload to
storage' feature in pve.

this change allows the endpoint to delete the file itself,
making the old and racey`sleep 1` (waiting until the worker
has opened the file) obsolete.

this change breaks all pve-manager versions, in which the
worker does not unlink the temp file itself.

Signed-off-by: Lorenz Stechauner <l.stechauner@proxmox.com>
---
 src/PVE/APIServer/AnyEvent.pm | 8 ++++----
 1 file changed, 4 insertions(+), 4 deletions(-)

diff --git a/src/PVE/APIServer/AnyEvent.pm b/src/PVE/APIServer/AnyEvent.pm
index cd77806..86d0e2e 100644
--- a/src/PVE/APIServer/AnyEvent.pm
+++ b/src/PVE/APIServer/AnyEvent.pm
@@ -114,7 +114,7 @@ sub log_aborted_request {
 }
 
 sub cleanup_reqstate {
-    my ($reqstate) = @_;
+    my ($reqstate, $deletetmpfile) = @_;
 
     delete $reqstate->{log};
     delete $reqstate->{request};
@@ -123,7 +123,7 @@ sub cleanup_reqstate {
     delete $reqstate->{starttime};
 
     if ($reqstate->{tmpfilename}) {
-	unlink $reqstate->{tmpfilename};
+	unlink $reqstate->{tmpfilename} if $deletetmpfile;
 	delete $reqstate->{tmpfilename};
     }
 }
@@ -131,7 +131,7 @@ sub cleanup_reqstate {
 sub client_do_disconnect {
     my ($self, $reqstate) = @_;
 
-    cleanup_reqstate($reqstate);
+    cleanup_reqstate($reqstate, 1);
 
     my $shutdown_hdl = sub {
 	my $hdl = shift;
@@ -168,7 +168,7 @@ sub client_do_disconnect {
 sub finish_response {
     my ($self, $reqstate) = @_;
 
-    cleanup_reqstate($reqstate);
+    cleanup_reqstate($reqstate, 0);
 
     my $hdl = $reqstate->{hdl};
     return if !$hdl; # already disconnected
-- 
2.30.2





^ permalink raw reply	[flat|nested] 17+ messages in thread

* [pve-devel] [PATCH v4 storage 1/4] status: move unlink from http-server to enpoint
  2021-08-31 10:16 [pve-devel] [PATCH-SERIES v4 http-server/storage/manager] fix #3505: add checksum and algorithm to iso upload Lorenz Stechauner
  2021-08-31 10:16 ` [pve-devel] [PATCH v4 http-server 1/1] anyevent: move unlink from http-server to endpoint Lorenz Stechauner
@ 2021-08-31 10:16 ` Lorenz Stechauner
  2021-09-06  7:04   ` [pve-devel] applied: " Thomas Lamprecht
  2021-08-31 10:16 ` [pve-devel] [PATCH v4 storage 2/4] status: remove sleep(1) in file upload Lorenz Stechauner
                   ` (7 subsequent siblings)
  9 siblings, 1 reply; 17+ messages in thread
From: Lorenz Stechauner @ 2021-08-31 10:16 UTC (permalink / raw)
  To: pve-devel

this is the first step in which not the http server removes the
temporary file, but the worker itself.

Signed-off-by: Lorenz Stechauner <l.stechauner@proxmox.com>
---
 PVE/API2/Storage/Status.pm | 6 ++++++
 1 file changed, 6 insertions(+)

diff --git a/PVE/API2/Storage/Status.pm b/PVE/API2/Storage/Status.pm
index b838461..cc8ed5a 100644
--- a/PVE/API2/Storage/Status.pm
+++ b/PVE/API2/Storage/Status.pm
@@ -486,6 +486,12 @@ __PACKAGE__->register_method ({
 	    print "command: " . join(' ', @$cmd) . "\n";
 
 	    eval { run_command($cmd, errmsg => 'import failed'); };
+
+	    # unlinks only the temporary file from the http server
+	    unlink $tmpfilename;
+	    warn "unable to clean up temporary file '$tmpfilename' - $!\n"
+		if $! && $! != ENOENT;
+
 	    if (my $err = $@) {
 		eval { $err_cleanup->() };
 		warn "$@" if $@;
-- 
2.30.2





^ permalink raw reply	[flat|nested] 17+ messages in thread

* [pve-devel] [PATCH v4 storage 2/4] status: remove sleep(1) in file upload
  2021-08-31 10:16 [pve-devel] [PATCH-SERIES v4 http-server/storage/manager] fix #3505: add checksum and algorithm to iso upload Lorenz Stechauner
  2021-08-31 10:16 ` [pve-devel] [PATCH v4 http-server 1/1] anyevent: move unlink from http-server to endpoint Lorenz Stechauner
  2021-08-31 10:16 ` [pve-devel] [PATCH v4 storage 1/4] status: move unlink from http-server to enpoint Lorenz Stechauner
@ 2021-08-31 10:16 ` Lorenz Stechauner
  2021-08-31 10:16 ` [pve-devel] [PATCH v4 storage 3/4] fix #3505: status: add checksum and algorithm to " Lorenz Stechauner
                   ` (6 subsequent siblings)
  9 siblings, 0 replies; 17+ messages in thread
From: Lorenz Stechauner @ 2021-08-31 10:16 UTC (permalink / raw)
  To: pve-devel

this racey sleep(1) is only there for legacy reasons: because
we don't use apache anymore and only emulate its behabiour
regarding removing temp files, this is under our own control
now and so we can improve this whole situation.

this change requires a pve-http-server version, in which the
tmpfile gets not automatically removed anymore.

Signed-off-by: Lorenz Stechauner <l.stechauner@proxmox.com>
---
 PVE/API2/Storage/Status.pm | 9 +--------
 1 file changed, 1 insertion(+), 8 deletions(-)

diff --git a/PVE/API2/Storage/Status.pm b/PVE/API2/Storage/Status.pm
index cc8ed5a..6493d22 100644
--- a/PVE/API2/Storage/Status.pm
+++ b/PVE/API2/Storage/Status.pm
@@ -500,14 +500,7 @@ __PACKAGE__->register_method ({
 	    print "finished file import successfully\n";
 	};
 
-	my $upid = $rpcenv->fork_worker('imgcopy', undef, $user, $worker);
-
-	# apache removes the temporary file on return, so we need
-	# to wait here to make sure the worker process starts and
-	# opens the file before it gets removed.
-	sleep(1);
-
-	return $upid;
+	return $rpcenv->fork_worker('imgcopy', undef, $user, $worker);
    }});
 
 __PACKAGE__->register_method({
-- 
2.30.2





^ permalink raw reply	[flat|nested] 17+ messages in thread

* [pve-devel] [PATCH v4 storage 3/4] fix #3505: status: add checksum and algorithm to file upload
  2021-08-31 10:16 [pve-devel] [PATCH-SERIES v4 http-server/storage/manager] fix #3505: add checksum and algorithm to iso upload Lorenz Stechauner
                   ` (2 preceding siblings ...)
  2021-08-31 10:16 ` [pve-devel] [PATCH v4 storage 2/4] status: remove sleep(1) in file upload Lorenz Stechauner
@ 2021-08-31 10:16 ` Lorenz Stechauner
  2021-08-31 10:16 ` [pve-devel] [PATCH v4 storage 4/4] status: fix tmpfile cleanup Lorenz Stechauner
                   ` (5 subsequent siblings)
  9 siblings, 0 replies; 17+ messages in thread
From: Lorenz Stechauner @ 2021-08-31 10:16 UTC (permalink / raw)
  To: pve-devel

Signed-off-by: Lorenz Stechauner <l.stechauner@proxmox.com>
---
 PVE/API2/Storage/Status.pm | 37 +++++++++++++++++++++++++++++++++++++
 1 file changed, 37 insertions(+)

diff --git a/PVE/API2/Storage/Status.pm b/PVE/API2/Storage/Status.pm
index 6493d22..41b3696 100644
--- a/PVE/API2/Storage/Status.pm
+++ b/PVE/API2/Storage/Status.pm
@@ -388,6 +388,19 @@ __PACKAGE__->register_method ({
 		maxLength => 255,
 		type => 'string',
 	    },
+	    checksum => {
+		description => "The expected checksum of the file.",
+		type => 'string',
+		requires => 'checksum-algorithm',
+		optional => 1,
+	    },
+	    'checksum-algorithm' => {
+		description => "The algorithm to calculate the checksum of the file.",
+		type => 'string',
+		enum => ['md5', 'sha1', 'sha224', 'sha256', 'sha384', 'sha512'],
+		requires => 'checksum',
+		optional => 1,
+	    },
 	    tmpfilename => {
 		description => "The source file name. This parameter is usually set by the REST handler. You can only overwrite it when connecting to the trusted port on localhost.",
 		type => 'string',
@@ -480,6 +493,30 @@ __PACKAGE__->register_method ({
 	    my $upid = shift;
 
 	    print "starting file import from: $tmpfilename\n";
+
+	    eval {
+		my ($checksum, $checksum_algorithm) = $param->@{'checksum', 'checksum-algorithm'};
+		if ($checksum_algorithm) {
+		    print "calculating checksum...";
+
+		    my $checksum_got = PVE::Tools::get_file_hash($checksum_algorithm, $tmpfilename);
+
+		    if (lc($checksum_got) eq lc($checksum)) {
+			print "OK, checksum verified\n";
+		    } else {
+			print "\n";  # the front end expects the error to reside at the last line without any noise
+			die "checksum mismatch: got '$checksum_got' != expect '$checksum'\n";
+		    }
+		}
+	    };
+	    if (my $err = $@) {
+		# unlinks only the temporary file from the http server
+		unlink $tmpfilename;
+		warn "unable to clean up temporory file '$tmpfilename' - $!\n"
+		    if $! && $! != ENOENT;
+		die $err;
+	    }
+
 	    print "target node: $node\n";
 	    print "target file: $dest\n";
 	    print "file size is: $size\n";
-- 
2.30.2





^ permalink raw reply	[flat|nested] 17+ messages in thread

* [pve-devel] [PATCH v4 storage 4/4] status: fix tmpfile cleanup
  2021-08-31 10:16 [pve-devel] [PATCH-SERIES v4 http-server/storage/manager] fix #3505: add checksum and algorithm to iso upload Lorenz Stechauner
                   ` (3 preceding siblings ...)
  2021-08-31 10:16 ` [pve-devel] [PATCH v4 storage 3/4] fix #3505: status: add checksum and algorithm to " Lorenz Stechauner
@ 2021-08-31 10:16 ` Lorenz Stechauner
  2021-11-04 15:33   ` [pve-devel] partially applied: " Thomas Lamprecht
  2021-08-31 10:16 ` [pve-devel] [PATCH v4 manager 1/5] ui: move upload window into UploadToStorage.js Lorenz Stechauner
                   ` (4 subsequent siblings)
  9 siblings, 1 reply; 17+ messages in thread
From: Lorenz Stechauner @ 2021-08-31 10:16 UTC (permalink / raw)
  To: pve-devel

$tmpfilename already gets unlinked after executing the cmd.

furthermore, because this is a local file, it is wrong to delete
it via the ssh command on a remote node.

small change: added \n to the error message.

Signed-off-by: Lorenz Stechauner <l.stechauner@proxmox.com>
---
 PVE/API2/Storage/Status.pm | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/PVE/API2/Storage/Status.pm b/PVE/API2/Storage/Status.pm
index 41b3696..5913281 100644
--- a/PVE/API2/Storage/Status.pm
+++ b/PVE/API2/Storage/Status.pm
@@ -459,7 +459,7 @@ __PACKAGE__->register_method ({
 	# best effort to match apl_download behaviour
 	chmod 0644, $tmpfilename;
 
-	my $err_cleanup = sub { unlink $dest, $tmpfilename; die "cleanup failed: $!" if $! && $! != ENOENT };
+	my $err_cleanup = sub { unlink $dest; die "cleanup failed: $!\n" if $! && $! != ENOENT };
 
 	my $cmd;
 	if ($node ne 'localhost' && $node ne PVE::INotify::nodename()) {
@@ -481,7 +481,7 @@ __PACKAGE__->register_method ({
  
 	    $cmd = ['/usr/bin/scp', @ssh_options, '-p', '--', $tmpfilename, "[$remip]:" . PVE::Tools::shell_quote($dest)];
 
-	    $err_cleanup = sub { run_command([@remcmd, 'rm', '-f', '--', $dest, $tmpfilename]) };
+	    $err_cleanup = sub { run_command([@remcmd, 'rm', '-f', '--', $dest]) };
 	} else {
 	    PVE::Storage::activate_storage($cfg, $param->{storage});
 	    File::Path::make_path($dirname);
-- 
2.30.2





^ permalink raw reply	[flat|nested] 17+ messages in thread

* [pve-devel] [PATCH v4 manager 1/5] ui: move upload window into UploadToStorage.js
  2021-08-31 10:16 [pve-devel] [PATCH-SERIES v4 http-server/storage/manager] fix #3505: add checksum and algorithm to iso upload Lorenz Stechauner
                   ` (4 preceding siblings ...)
  2021-08-31 10:16 ` [pve-devel] [PATCH v4 storage 4/4] status: fix tmpfile cleanup Lorenz Stechauner
@ 2021-08-31 10:16 ` Lorenz Stechauner
  2021-11-05  8:12   ` Dominik Csapak
  2021-08-31 10:16 ` [pve-devel] [PATCH v4 manager 2/5] ui: refactor UploadToStorage.js Lorenz Stechauner
                   ` (3 subsequent siblings)
  9 siblings, 1 reply; 17+ messages in thread
From: Lorenz Stechauner @ 2021-08-31 10:16 UTC (permalink / raw)
  To: pve-devel

Signed-off-by: Lorenz Stechauner <l.stechauner@proxmox.com>
---
 www/manager6/Makefile                  |   1 +
 www/manager6/storage/ContentView.js    | 195 +------------------------
 www/manager6/window/UploadToStorage.js | 192 ++++++++++++++++++++++++
 3 files changed, 194 insertions(+), 194 deletions(-)
 create mode 100644 www/manager6/window/UploadToStorage.js

diff --git a/www/manager6/Makefile b/www/manager6/Makefile
index 75d355a5..552e842b 100644
--- a/www/manager6/Makefile
+++ b/www/manager6/Makefile
@@ -106,6 +106,7 @@ JSSRC= 							\
 	window/Snapshot.js				\
 	window/StartupEdit.js				\
 	window/DownloadUrlToStorage.js 			\
+	window/UploadToStorage.js 			\
 	window/Wizard.js				\
 	ha/Fencing.js					\
 	ha/GroupEdit.js					\
diff --git a/www/manager6/storage/ContentView.js b/www/manager6/storage/ContentView.js
index 3f5b686b..ca0ad664 100644
--- a/www/manager6/storage/ContentView.js
+++ b/www/manager6/storage/ContentView.js
@@ -1,196 +1,3 @@
-Ext.define('PVE.storage.Upload', {
-    extend: 'Ext.window.Window',
-    alias: 'widget.pveStorageUpload',
-
-    resizable: false,
-
-    modal: true,
-
-    initComponent: function() {
-        var me = this;
-
-	if (!me.nodename) {
-	    throw "no node name specified";
-	}
-	if (!me.storage) {
-	    throw "no storage ID specified";
-	}
-
-	let baseurl = `/nodes/${me.nodename}/storage/${me.storage}/upload`;
-
-	let pbar = Ext.create('Ext.ProgressBar', {
-            text: 'Ready',
-	    hidden: true,
-	});
-
-	let acceptedExtensions = {
-	    iso: ".img, .iso",
-	    vztmpl: ".tar.gz, .tar.xz",
-	};
-
-	let defaultContent = me.contents[0] || '';
-
-	let fileField = Ext.create('Ext.form.field.File', {
-	    name: 'filename',
-	    buttonText: gettext('Select File...'),
-	    allowBlank: false,
-	    setAccept: function(content) {
-		let acceptString = acceptedExtensions[content] || '';
-		this.fileInputEl.set({
-		    accept: acceptString,
-		});
-	    },
-	    listeners: {
-		afterrender: function(cmp) {
-		    cmp.setAccept(defaultContent);
-		},
-	    },
-	});
-
-	me.formPanel = Ext.create('Ext.form.Panel', {
-	    method: 'POST',
-	    waitMsgTarget: true,
-	    bodyPadding: 10,
-	    border: false,
-	    width: 300,
-	    fieldDefaults: {
-		labelWidth: 100,
-		anchor: '100%',
-            },
-	    items: [
-		{
-		    xtype: 'pveContentTypeSelector',
-		    cts: me.contents,
-		    fieldLabel: gettext('Content'),
-		    name: 'content',
-		    value: defaultContent,
-		    allowBlank: false,
-		    listeners: {
-			change: function(cmp, newValue, oldValue) {
-			    fileField.setAccept(newValue);
-			},
-		    },
-		},
-		fileField,
-		pbar,
-	    ],
-	});
-
-	let form = me.formPanel.getForm();
-
-	let doStandardSubmit = function() {
-	    form.submit({
-		url: "/api2/htmljs" + baseurl,
-		waitMsg: gettext('Uploading file...'),
-		success: function(f, action) {
-		    me.close();
-		},
-		failure: function(f, action) {
-		    var msg = PVE.Utils.extractFormActionError(action);
-                    Ext.Msg.alert(gettext('Error'), msg);
-		},
-	    });
-	};
-
-	let updateProgress = function(per, bytes) {
-	    var text = (per * 100).toFixed(2) + '%';
-	    if (bytes) {
-		text += " (" + Proxmox.Utils.format_size(bytes) + ')';
-	    }
-	    pbar.updateProgress(per, text);
-	};
-
-	let abortBtn = Ext.create('Ext.Button', {
-	    text: gettext('Abort'),
-	    disabled: true,
-	    handler: function() {
-		me.close();
-	    },
-	});
-
-	let submitBtn = Ext.create('Ext.Button', {
-	    text: gettext('Upload'),
-	    disabled: true,
-	    handler: function(button) {
-		var fd;
-		try {
-		    fd = new FormData();
-		} catch (err) {
-		    doStandardSubmit();
-		    return;
-		}
-
-		button.setDisabled(true);
-		abortBtn.setDisabled(false);
-
-		var field = form.findField('content');
-		fd.append("content", field.getValue());
-		field.setDisabled(true);
-
-		field = form.findField('filename');
-		var file = field.fileInputEl.dom;
-		fd.append("filename", file.files[0]);
-		field.setDisabled(true);
-
-		pbar.setVisible(true);
-		updateProgress(0);
-
-		let xhr = new XMLHttpRequest();
-		me.xhr = xhr;
-
-		xhr.addEventListener("load", function(e) {
-		    if (xhr.status === 200) {
-			me.close();
-			return;
-		    }
-		    let err = Ext.htmlEncode(xhr.statusText);
-		    let msg = `${gettext('Error')} ${xhr.status.toString()}: ${err}`;
-		    if (xhr.responseText !== "") {
-			let result = Ext.decode(xhr.responseText);
-			result.message = msg;
-			msg = Proxmox.Utils.extractRequestError(result, true);
-		    }
-		    Ext.Msg.alert(gettext('Error'), msg, btn => me.close());
-		}, false);
-
-		xhr.addEventListener("error", function(e) {
-		    let err = e.target.status.toString();
-		    let msg = `Error '${err}' occurred while receiving the document.`;
-		    Ext.Msg.alert(gettext('Error'), msg, btn => me.close());
-		});
-
-		xhr.upload.addEventListener("progress", function(evt) {
-		    if (evt.lengthComputable) {
-			let percentComplete = evt.loaded / evt.total;
-			updateProgress(percentComplete, evt.loaded);
-		    }
-		}, false);
-
-		xhr.open("POST", `/api2/json${baseurl}`, true);
-		xhr.send(fd);
-	    },
-	});
-
-	form.on('validitychange', (f, valid) => submitBtn.setDisabled(!valid));
-
-	Ext.apply(me, {
-	    title: gettext('Upload'),
-	    items: me.formPanel,
-	    buttons: [abortBtn, submitBtn],
-	    listeners: {
-		close: function() {
-		    if (me.xhr) {
-			me.xhr.abort();
-			delete me.xhr;
-		    }
-		},
-	    },
-	});
-
-        me.callParent();
-    },
-});
-
 Ext.define('PVE.storage.ContentView', {
     extend: 'Ext.grid.GridPanel',
 
@@ -259,7 +66,7 @@ Ext.define('PVE.storage.ContentView', {
 		    text: gettext('Upload'),
 		    disabled: !me.enableUploadButton,
 		    handler: function() {
-			Ext.create('PVE.storage.Upload', {
+			Ext.create('PVE.window.UploadToStorage', {
 			    nodename: nodename,
 			    storage: storage,
 			    contents: [content],
diff --git a/www/manager6/window/UploadToStorage.js b/www/manager6/window/UploadToStorage.js
new file mode 100644
index 00000000..3c35020a
--- /dev/null
+++ b/www/manager6/window/UploadToStorage.js
@@ -0,0 +1,192 @@
+Ext.define('PVE.window.UploadToStorage', {
+    extend: 'Ext.window.Window',
+    alias: 'widget.pveStorageUpload',
+
+    resizable: false,
+
+    modal: true,
+
+    initComponent: function() {
+        var me = this;
+
+	if (!me.nodename) {
+	    throw "no node name specified";
+	}
+	if (!me.storage) {
+	    throw "no storage ID specified";
+	}
+
+	let baseurl = `/nodes/${me.nodename}/storage/${me.storage}/upload`;
+
+	let pbar = Ext.create('Ext.ProgressBar', {
+            text: 'Ready',
+	    hidden: true,
+	});
+
+	let acceptedExtensions = {
+	    iso: ".img, .iso",
+	    vztmpl: ".tar.gz, .tar.xz",
+	};
+
+	let defaultContent = me.contents[0] || '';
+
+	let fileField = Ext.create('Ext.form.field.File', {
+	    name: 'filename',
+	    buttonText: gettext('Select File...'),
+	    allowBlank: false,
+	    setAccept: function(content) {
+		let acceptString = acceptedExtensions[content] || '';
+		this.fileInputEl.set({
+		    accept: acceptString,
+		});
+	    },
+	    listeners: {
+		afterrender: function(cmp) {
+		    cmp.setAccept(defaultContent);
+		},
+	    },
+	});
+
+	me.formPanel = Ext.create('Ext.form.Panel', {
+	    method: 'POST',
+	    waitMsgTarget: true,
+	    bodyPadding: 10,
+	    border: false,
+	    width: 300,
+	    fieldDefaults: {
+		labelWidth: 100,
+		anchor: '100%',
+            },
+	    items: [
+		{
+		    xtype: 'pveContentTypeSelector',
+		    cts: me.contents,
+		    fieldLabel: gettext('Content'),
+		    name: 'content',
+		    value: defaultContent,
+		    allowBlank: false,
+		    listeners: {
+			change: function(cmp, newValue, oldValue) {
+			    fileField.setAccept(newValue);
+			},
+		    },
+		},
+		fileField,
+		pbar,
+	    ],
+	});
+
+	let form = me.formPanel.getForm();
+
+	let doStandardSubmit = function() {
+	    form.submit({
+		url: "/api2/htmljs" + baseurl,
+		waitMsg: gettext('Uploading file...'),
+		success: function(f, action) {
+		    me.close();
+		},
+		failure: function(f, action) {
+		    var msg = PVE.Utils.extractFormActionError(action);
+                    Ext.Msg.alert(gettext('Error'), msg);
+		},
+	    });
+	};
+
+	let updateProgress = function(per, bytes) {
+	    var text = (per * 100).toFixed(2) + '%';
+	    if (bytes) {
+		text += " (" + Proxmox.Utils.format_size(bytes) + ')';
+	    }
+	    pbar.updateProgress(per, text);
+	};
+
+	let abortBtn = Ext.create('Ext.Button', {
+	    text: gettext('Abort'),
+	    disabled: true,
+	    handler: function() {
+		me.close();
+	    },
+	});
+
+	let submitBtn = Ext.create('Ext.Button', {
+	    text: gettext('Upload'),
+	    disabled: true,
+	    handler: function(button) {
+		var fd;
+		try {
+		    fd = new FormData();
+		} catch (err) {
+		    doStandardSubmit();
+		    return;
+		}
+
+		button.setDisabled(true);
+		abortBtn.setDisabled(false);
+
+		var field = form.findField('content');
+		fd.append("content", field.getValue());
+		field.setDisabled(true);
+
+		field = form.findField('filename');
+		var file = field.fileInputEl.dom;
+		fd.append("filename", file.files[0]);
+		field.setDisabled(true);
+
+		pbar.setVisible(true);
+		updateProgress(0);
+
+		let xhr = new XMLHttpRequest();
+		me.xhr = xhr;
+
+		xhr.addEventListener("load", function(e) {
+		    if (xhr.status === 200) {
+			me.close();
+			return;
+		    }
+		    let err = Ext.htmlEncode(xhr.statusText);
+		    let msg = `${gettext('Error')} ${xhr.status.toString()}: ${err}`;
+		    if (xhr.responseText !== "") {
+			let result = Ext.decode(xhr.responseText);
+			result.message = msg;
+			msg = Proxmox.Utils.extractRequestError(result, true);
+		    }
+		    Ext.Msg.alert(gettext('Error'), msg, btn => me.close());
+		}, false);
+
+		xhr.addEventListener("error", function(e) {
+		    let err = e.target.status.toString();
+		    let msg = `Error '${err}' occurred while receiving the document.`;
+		    Ext.Msg.alert(gettext('Error'), msg, btn => me.close());
+		});
+
+		xhr.upload.addEventListener("progress", function(evt) {
+		    if (evt.lengthComputable) {
+			let percentComplete = evt.loaded / evt.total;
+			updateProgress(percentComplete, evt.loaded);
+		    }
+		}, false);
+
+		xhr.open("POST", `/api2/json${baseurl}`, true);
+		xhr.send(fd);
+	    },
+	});
+
+	form.on('validitychange', (f, valid) => submitBtn.setDisabled(!valid));
+
+	Ext.apply(me, {
+	    title: gettext('Upload'),
+	    items: me.formPanel,
+	    buttons: [abortBtn, submitBtn],
+	    listeners: {
+		close: function() {
+		    if (me.xhr) {
+			me.xhr.abort();
+			delete me.xhr;
+		    }
+		},
+	    },
+	});
+
+        me.callParent();
+    },
+});
-- 
2.30.2





^ permalink raw reply	[flat|nested] 17+ messages in thread

* [pve-devel] [PATCH v4 manager 2/5] ui: refactor UploadToStorage.js
  2021-08-31 10:16 [pve-devel] [PATCH-SERIES v4 http-server/storage/manager] fix #3505: add checksum and algorithm to iso upload Lorenz Stechauner
                   ` (5 preceding siblings ...)
  2021-08-31 10:16 ` [pve-devel] [PATCH v4 manager 1/5] ui: move upload window into UploadToStorage.js Lorenz Stechauner
@ 2021-08-31 10:16 ` Lorenz Stechauner
  2021-11-05  8:12   ` Dominik Csapak
  2021-08-31 10:16 ` [pve-devel] [PATCH v4 manager 3/5] fix #3505: ui/UploadToStorage: add checksum and algorithm Lorenz Stechauner
                   ` (2 subsequent siblings)
  9 siblings, 1 reply; 17+ messages in thread
From: Lorenz Stechauner @ 2021-08-31 10:16 UTC (permalink / raw)
  To: pve-devel

this also removes the "content" selector from the window.
as far as it seems, this selector was never able to select
more than one entry, so it was useless.

the check for FormData() is also removed, because this is
supported by all major browsers for a long time. therefore
doStandardSubmit() is also not necessary.

Signed-off-by: Lorenz Stechauner <l.stechauner@proxmox.com>
---
 www/manager6/storage/ContentView.js    |   2 +-
 www/manager6/window/UploadToStorage.js | 346 ++++++++++++++-----------
 2 files changed, 196 insertions(+), 152 deletions(-)

diff --git a/www/manager6/storage/ContentView.js b/www/manager6/storage/ContentView.js
index ca0ad664..00a94f3c 100644
--- a/www/manager6/storage/ContentView.js
+++ b/www/manager6/storage/ContentView.js
@@ -69,7 +69,7 @@ Ext.define('PVE.storage.ContentView', {
 			Ext.create('PVE.window.UploadToStorage', {
 			    nodename: nodename,
 			    storage: storage,
-			    contents: [content],
+			    content: content,
 			    autoShow: true,
 			    taskDone: () => reload(),
 			});
diff --git a/www/manager6/window/UploadToStorage.js b/www/manager6/window/UploadToStorage.js
index 3c35020a..e7db41fb 100644
--- a/www/manager6/window/UploadToStorage.js
+++ b/www/manager6/window/UploadToStorage.js
@@ -1,191 +1,235 @@
 Ext.define('PVE.window.UploadToStorage', {
     extend: 'Ext.window.Window',
     alias: 'widget.pveStorageUpload',
+    mixins: ['Proxmox.Mixin.CBind'],
 
     resizable: false,
-
     modal: true,
 
-    initComponent: function() {
-        var me = this;
+    title: gettext('Upload'),
 
-	if (!me.nodename) {
-	    throw "no node name specified";
-	}
-	if (!me.storage) {
-	    throw "no storage ID specified";
-	}
+    acceptedExtensions: {
+	iso: ['.img', '.iso'],
+	vztmpl: ['.tar.gz', '.tar.xz'],
+    },
 
-	let baseurl = `/nodes/${me.nodename}/storage/${me.storage}/upload`;
+    cbindData: function(initialConfig) {
+	const me = this;
+	const ext = me.acceptedExtensions[me.content];
 
-	let pbar = Ext.create('Ext.ProgressBar', {
-            text: 'Ready',
-	    hidden: true,
-	});
+	me.url = `/nodes/${me.nodename}/storage/${me.storage}/upload`;
 
-	let acceptedExtensions = {
-	    iso: ".img, .iso",
-	    vztmpl: ".tar.gz, .tar.xz",
+	return {
+	    extensions: ext.join(', '),
 	};
+    },
 
-	let defaultContent = me.contents[0] || '';
-
-	let fileField = Ext.create('Ext.form.field.File', {
-	    name: 'filename',
-	    buttonText: gettext('Select File...'),
-	    allowBlank: false,
-	    setAccept: function(content) {
-		let acceptString = acceptedExtensions[content] || '';
-		this.fileInputEl.set({
-		    accept: acceptString,
-		});
-	    },
-	    listeners: {
-		afterrender: function(cmp) {
-		    cmp.setAccept(defaultContent);
-		},
-	    },
-	});
+    viewModel: {
+	data: {
+	    size: '-',
+	    mimetype: '-',
+	    filename: '',
+	},
+    },
+
+    controller: {
+	submit: function(button) {
+	    const view = this.getView();
+	    const form = this.lookup('formPanel').getForm();
+	    const abortBtn = this.lookup('abortBtn');
+	    const pbar = this.lookup('progressBar');
+
+	    const updateProgress = function(per, bytes) {
+		let text = (per * 100).toFixed(2) + '%';
+		if (bytes) {
+		    text += " (" + Proxmox.Utils.format_size(bytes) + ')';
+		}
+		pbar.updateProgress(per, text);
+	    };
+
+	    const fd = new FormData();
+
+	    button.setDisabled(true);
+	    abortBtn.setDisabled(false);
+
+	    fd.append("content", view.content);
 
-	me.formPanel = Ext.create('Ext.form.Panel', {
+	    const fileField = form.findField('file');
+	    const file = fileField.fileInputEl.dom.files[0];
+	    fileField.setDisabled(true);
+
+	    const filenameField = form.findField('filename');
+	    const filename = filenameField.getValue();
+	    filenameField.setDisabled(true);
+
+	    fd.append("filename", file, filename);
+
+	    pbar.setVisible(true);
+	    updateProgress(0);
+
+	    const xhr = new XMLHttpRequest();
+	    view.xhr = xhr;
+
+	    xhr.addEventListener("load", function(e) {
+		if (xhr.status === 200) {
+		    view.close();
+		    return;
+		}
+		const err = Ext.htmlEncode(xhr.statusText);
+		let msg = `${gettext('Error')} ${xhr.status.toString()}: ${err}`;
+		if (xhr.responseText !== "") {
+		    const result = Ext.decode(xhr.responseText);
+		    result.message = msg;
+		    msg = Proxmox.Utils.extractRequestError(result, true);
+		}
+		Ext.Msg.alert(gettext('Error'), msg, btn => view.close());
+	    }, false);
+
+	    xhr.addEventListener("error", function(e) {
+		const err = e.target.status.toString();
+		const msg = `Error '${err}' occurred while receiving the document.`;
+		Ext.Msg.alert(gettext('Error'), msg, btn => view.close());
+	    });
+
+	    xhr.upload.addEventListener("progress", function(evt) {
+		if (evt.lengthComputable) {
+		    const percentComplete = evt.loaded / evt.total;
+		    updateProgress(percentComplete, evt.loaded);
+		}
+	    }, false);
+
+	    xhr.open("POST", `/api2/json${view.url}`, true);
+	    xhr.send(fd);
+	},
+
+	validitychange: function(f, valid) {
+	    const submitBtn = this.lookup('submitBtn');
+	    submitBtn.setDisabled(!valid);
+	},
+
+	fileChange: function(input) {
+	    const vm = this.getViewModel();
+	    const name = input.value.replace(/^.*(\/|\\)/, '');
+	    const fileInput = input.fileInputEl.dom;
+	    vm.set('filename', name);
+	    vm.set('size', (fileInput.files[0] && Proxmox.Utils.format_size(fileInput.files[0].size)) || '-');
+	    vm.set('mimetype', (fileInput.files[0] && fileInput.files[0].type) || '-');
+	},
+    },
+
+    items: [
+	{
+	    xtype: 'form',
+	    reference: 'formPanel',
 	    method: 'POST',
 	    waitMsgTarget: true,
 	    bodyPadding: 10,
 	    border: false,
-	    width: 300,
+	    width: 400,
 	    fieldDefaults: {
 		labelWidth: 100,
 		anchor: '100%',
             },
 	    items: [
 		{
-		    xtype: 'pveContentTypeSelector',
-		    cts: me.contents,
-		    fieldLabel: gettext('Content'),
-		    name: 'content',
-		    value: defaultContent,
+		    xtype: 'filefield',
+		    name: 'file',
+		    buttonText: gettext('Select File'),
 		    allowBlank: false,
+		    fieldLabel: gettext('File'),
+		    cbind: {
+			accept: '{extensions}',
+		    },
 		    listeners: {
-			change: function(cmp, newValue, oldValue) {
-			    fileField.setAccept(newValue);
-			},
+			change: 'fileChange',
 		    },
 		},
-		fileField,
-		pbar,
-	    ],
-	});
-
-	let form = me.formPanel.getForm();
-
-	let doStandardSubmit = function() {
-	    form.submit({
-		url: "/api2/htmljs" + baseurl,
-		waitMsg: gettext('Uploading file...'),
-		success: function(f, action) {
-		    me.close();
+		{
+		    xtype: 'textfield',
+		    name: 'filename',
+		    allowBlank: false,
+		    fieldLabel: gettext('File name'),
+		    bind: {
+			value: '{filename}',
+		    },
 		},
-		failure: function(f, action) {
-		    var msg = PVE.Utils.extractFormActionError(action);
-                    Ext.Msg.alert(gettext('Error'), msg);
+		{
+		    xtype: 'displayfield',
+		    name: 'size',
+		    fieldLabel: gettext('File size'),
+		    bind: {
+			value: '{size}',
+		    },
 		},
-	    });
-	};
-
-	let updateProgress = function(per, bytes) {
-	    var text = (per * 100).toFixed(2) + '%';
-	    if (bytes) {
-		text += " (" + Proxmox.Utils.format_size(bytes) + ')';
-	    }
-	    pbar.updateProgress(per, text);
-	};
-
-	let abortBtn = Ext.create('Ext.Button', {
+		{
+		    xtype: 'displayfield',
+		    name: 'mimetype',
+		    fieldLabel: gettext('MIME type'),
+		    bind: {
+			value: '{mimetype}',
+		    },
+		},
+		{
+		    xtype: 'progressbar',
+		    text: 'Ready',
+		    hidden: true,
+		    reference: 'progressBar',
+		},
+		{
+		    xtype: 'hiddenfield',
+		    name: 'content',
+		    cbind: {
+			value: '{content}',
+		    },
+		},
+	    ],
+	   listeners: {
+		validitychange: 'validitychange',
+	   },
+	},
+    ],
+
+    buttons: [
+	{
+	    xtype: 'button',
 	    text: gettext('Abort'),
+	    reference: 'abortBtn',
 	    disabled: true,
 	    handler: function() {
-		me.close();
+		const me = this;
+		me.up('pveStorageUpload').close();
 	    },
-	});
-
-	let submitBtn = Ext.create('Ext.Button', {
+	},
+	{
 	    text: gettext('Upload'),
+	    reference: 'submitBtn',
 	    disabled: true,
-	    handler: function(button) {
-		var fd;
-		try {
-		    fd = new FormData();
-		} catch (err) {
-		    doStandardSubmit();
-		    return;
-		}
+	    handler: 'submit',
+	},
+    ],
+
+    listeners: {
+	close: function() {
+	    const me = this;
+	    if (me.xhr) {
+		me.xhr.abort();
+		delete me.xhr;
+	    }
+	},
+    },
 
-		button.setDisabled(true);
-		abortBtn.setDisabled(false);
-
-		var field = form.findField('content');
-		fd.append("content", field.getValue());
-		field.setDisabled(true);
-
-		field = form.findField('filename');
-		var file = field.fileInputEl.dom;
-		fd.append("filename", file.files[0]);
-		field.setDisabled(true);
-
-		pbar.setVisible(true);
-		updateProgress(0);
-
-		let xhr = new XMLHttpRequest();
-		me.xhr = xhr;
-
-		xhr.addEventListener("load", function(e) {
-		    if (xhr.status === 200) {
-			me.close();
-			return;
-		    }
-		    let err = Ext.htmlEncode(xhr.statusText);
-		    let msg = `${gettext('Error')} ${xhr.status.toString()}: ${err}`;
-		    if (xhr.responseText !== "") {
-			let result = Ext.decode(xhr.responseText);
-			result.message = msg;
-			msg = Proxmox.Utils.extractRequestError(result, true);
-		    }
-		    Ext.Msg.alert(gettext('Error'), msg, btn => me.close());
-		}, false);
-
-		xhr.addEventListener("error", function(e) {
-		    let err = e.target.status.toString();
-		    let msg = `Error '${err}' occurred while receiving the document.`;
-		    Ext.Msg.alert(gettext('Error'), msg, btn => me.close());
-		});
-
-		xhr.upload.addEventListener("progress", function(evt) {
-		    if (evt.lengthComputable) {
-			let percentComplete = evt.loaded / evt.total;
-			updateProgress(percentComplete, evt.loaded);
-		    }
-		}, false);
-
-		xhr.open("POST", `/api2/json${baseurl}`, true);
-		xhr.send(fd);
-	    },
-	});
-
-	form.on('validitychange', (f, valid) => submitBtn.setDisabled(!valid));
-
-	Ext.apply(me, {
-	    title: gettext('Upload'),
-	    items: me.formPanel,
-	    buttons: [abortBtn, submitBtn],
-	    listeners: {
-		close: function() {
-		    if (me.xhr) {
-			me.xhr.abort();
-			delete me.xhr;
-		    }
-		},
-	    },
-	});
+    initComponent: function() {
+        const me = this;
+
+	if (!me.nodename) {
+	    throw "no node name specified";
+	}
+	if (!me.storage) {
+	    throw "no storage ID specified";
+	}
+	if (!(me.content in me.acceptedExtensions)) {
+	    throw "content type not supported";
+	}
 
         me.callParent();
     },
-- 
2.30.2





^ permalink raw reply	[flat|nested] 17+ messages in thread

* [pve-devel] [PATCH v4 manager 3/5] fix #3505: ui/UploadToStorage: add checksum and algorithm
  2021-08-31 10:16 [pve-devel] [PATCH-SERIES v4 http-server/storage/manager] fix #3505: add checksum and algorithm to iso upload Lorenz Stechauner
                   ` (6 preceding siblings ...)
  2021-08-31 10:16 ` [pve-devel] [PATCH v4 manager 2/5] ui: refactor UploadToStorage.js Lorenz Stechauner
@ 2021-08-31 10:16 ` Lorenz Stechauner
  2021-08-31 10:16 ` [pve-devel] [PATCH v4 manager 4/5] ui/UploadToStorage: add TaskViewer Lorenz Stechauner
  2021-08-31 10:16 ` [pve-devel] [PATCH v4 manager 5/5] ui/UplaodToStorage: check file extension Lorenz Stechauner
  9 siblings, 0 replies; 17+ messages in thread
From: Lorenz Stechauner @ 2021-08-31 10:16 UTC (permalink / raw)
  To: pve-devel

Signed-off-by: Lorenz Stechauner <l.stechauner@proxmox.com>
---
 www/manager6/window/UploadToStorage.js | 40 ++++++++++++++++++++++++++
 1 file changed, 40 insertions(+)

diff --git a/www/manager6/window/UploadToStorage.js b/www/manager6/window/UploadToStorage.js
index e7db41fb..489be9dc 100644
--- a/www/manager6/window/UploadToStorage.js
+++ b/www/manager6/window/UploadToStorage.js
@@ -62,6 +62,16 @@ Ext.define('PVE.window.UploadToStorage', {
 	    const filename = filenameField.getValue();
 	    filenameField.setDisabled(true);
 
+	    const algorithmField = form.findField('checksum-algorithm');
+	    algorithmField.setDisabled(true);
+	    if (algorithmField.getValue() !== '__default__') {
+		fd.append("checksum-algorithm", algorithmField.getValue());
+
+		const checksumField = form.findField('checksum');
+		fd.append("checksum", checksumField.getValue());
+		checksumField.setDisabled(true);
+	    }
+
 	    fd.append("filename", file, filename);
 
 	    pbar.setVisible(true);
@@ -115,6 +125,16 @@ Ext.define('PVE.window.UploadToStorage', {
 	    vm.set('size', (fileInput.files[0] && Proxmox.Utils.format_size(fileInput.files[0].size)) || '-');
 	    vm.set('mimetype', (fileInput.files[0] && fileInput.files[0].type) || '-');
 	},
+
+	hashChange: function(field, value) {
+	    const checksum = this.lookup('downloadUrlChecksum');
+	    if (value === '__default__') {
+		checksum.setDisabled(true);
+		checksum.setValue("");
+	    } else {
+		checksum.setDisabled(false);
+	    }
+	},
     },
 
     items: [
@@ -169,6 +189,26 @@ Ext.define('PVE.window.UploadToStorage', {
 			value: '{mimetype}',
 		    },
 		},
+		{
+		    xtype: 'pveHashAlgorithmSelector',
+		    name: 'checksum-algorithm',
+		    fieldLabel: gettext('Hash algorithm'),
+		    allowBlank: true,
+		    hasNoneOption: true,
+		    value: '__default__',
+		    listeners: {
+			change: 'hashChange',
+		    },
+		},
+		{
+		    xtype: 'textfield',
+		    name: 'checksum',
+		    fieldLabel: gettext('Checksum'),
+		    allowBlank: false,
+		    disabled: true,
+		    emptyText: gettext('none'),
+		    reference: 'downloadUrlChecksum',
+		},
 		{
 		    xtype: 'progressbar',
 		    text: 'Ready',
-- 
2.30.2





^ permalink raw reply	[flat|nested] 17+ messages in thread

* [pve-devel] [PATCH v4 manager 4/5] ui/UploadToStorage: add TaskViewer
  2021-08-31 10:16 [pve-devel] [PATCH-SERIES v4 http-server/storage/manager] fix #3505: add checksum and algorithm to iso upload Lorenz Stechauner
                   ` (7 preceding siblings ...)
  2021-08-31 10:16 ` [pve-devel] [PATCH v4 manager 3/5] fix #3505: ui/UploadToStorage: add checksum and algorithm Lorenz Stechauner
@ 2021-08-31 10:16 ` Lorenz Stechauner
  2021-08-31 10:16 ` [pve-devel] [PATCH v4 manager 5/5] ui/UplaodToStorage: check file extension Lorenz Stechauner
  9 siblings, 0 replies; 17+ messages in thread
From: Lorenz Stechauner @ 2021-08-31 10:16 UTC (permalink / raw)
  To: pve-devel

Signed-off-by: Lorenz Stechauner <l.stechauner@proxmox.com>
---
 www/manager6/window/UploadToStorage.js | 16 +++++++++++++++-
 1 file changed, 15 insertions(+), 1 deletion(-)

diff --git a/www/manager6/window/UploadToStorage.js b/www/manager6/window/UploadToStorage.js
index 489be9dc..f0c6186f 100644
--- a/www/manager6/window/UploadToStorage.js
+++ b/www/manager6/window/UploadToStorage.js
@@ -82,7 +82,21 @@ Ext.define('PVE.window.UploadToStorage', {
 
 	    xhr.addEventListener("load", function(e) {
 		if (xhr.status === 200) {
-		    view.close();
+		    view.hide();
+
+		    const result = JSON.parse(xhr.response);
+		    const upid = result.data;
+		    Ext.create('Proxmox.window.TaskViewer', {
+			autoShow: true,
+			upid: upid,
+			taskDone: view.taskDone,
+			listeners: {
+			    destroy: function() {
+				view.close();
+			    },
+			},
+		    });
+
 		    return;
 		}
 		const err = Ext.htmlEncode(xhr.statusText);
-- 
2.30.2





^ permalink raw reply	[flat|nested] 17+ messages in thread

* [pve-devel] [PATCH v4 manager 5/5] ui/UplaodToStorage: check file extension
  2021-08-31 10:16 [pve-devel] [PATCH-SERIES v4 http-server/storage/manager] fix #3505: add checksum and algorithm to iso upload Lorenz Stechauner
                   ` (8 preceding siblings ...)
  2021-08-31 10:16 ` [pve-devel] [PATCH v4 manager 4/5] ui/UploadToStorage: add TaskViewer Lorenz Stechauner
@ 2021-08-31 10:16 ` Lorenz Stechauner
  2021-11-05  8:12   ` Dominik Csapak
  9 siblings, 1 reply; 17+ messages in thread
From: Lorenz Stechauner @ 2021-08-31 10:16 UTC (permalink / raw)
  To: pve-devel

Signed-off-by: Lorenz Stechauner <l.stechauner@proxmox.com>
---
 www/manager6/window/UploadToStorage.js | 5 +++++
 1 file changed, 5 insertions(+)

diff --git a/www/manager6/window/UploadToStorage.js b/www/manager6/window/UploadToStorage.js
index f0c6186f..ec83c581 100644
--- a/www/manager6/window/UploadToStorage.js
+++ b/www/manager6/window/UploadToStorage.js
@@ -21,6 +21,7 @@ Ext.define('PVE.window.UploadToStorage', {
 
 	return {
 	    extensions: ext.join(', '),
+	    filenameRegex: RegExp('^.*(?:' + ext.join('|').replaceAll('.', '\\.') + ')$', 'i'),
 	};
     },
 
@@ -186,6 +187,10 @@ Ext.define('PVE.window.UploadToStorage', {
 		    bind: {
 			value: '{filename}',
 		    },
+		    cbind: {
+			regex: '{filenameRegex}',
+		    },
+		    regexText: "wrong file extension",
 		},
 		{
 		    xtype: 'displayfield',
-- 
2.30.2





^ permalink raw reply	[flat|nested] 17+ messages in thread

* [pve-devel] applied: [PATCH v4 storage 1/4] status: move unlink from http-server to enpoint
  2021-08-31 10:16 ` [pve-devel] [PATCH v4 storage 1/4] status: move unlink from http-server to enpoint Lorenz Stechauner
@ 2021-09-06  7:04   ` Thomas Lamprecht
  0 siblings, 0 replies; 17+ messages in thread
From: Thomas Lamprecht @ 2021-09-06  7:04 UTC (permalink / raw)
  To: Proxmox VE development discussion, Lorenz Stechauner

meh, only noticed the s/enpoint/endpoint/ spelling error in the subject after
pushing it out..

On 31.08.21 12:16, Lorenz Stechauner wrote:
> this is the first step in which not the http server removes the
> temporary file, but the worker itself.
> 
> Signed-off-by: Lorenz Stechauner <l.stechauner@proxmox.com>
> ---
>  PVE/API2/Storage/Status.pm | 6 ++++++
>  1 file changed, 6 insertions(+)
> 
> diff --git a/PVE/API2/Storage/Status.pm b/PVE/API2/Storage/Status.pm
> index b838461..cc8ed5a 100644
> --- a/PVE/API2/Storage/Status.pm
> +++ b/PVE/API2/Storage/Status.pm
> @@ -486,6 +486,12 @@ __PACKAGE__->register_method ({
>  	    print "command: " . join(' ', @$cmd) . "\n";
>  
>  	    eval { run_command($cmd, errmsg => 'import failed'); };
> +
> +	    # unlinks only the temporary file from the http server

this is just describing what I can read from the code below anyway? And it implies
to me that it removes the file from some directory a http server serves. I adapted
this trying to give a slightly better hint for a "why?".

"the temporary file got only uploaded locally, no need to rm remote"

> +	    unlink $tmpfilename;
> +	    warn "unable to clean up temporary file '$tmpfilename' - $!\n"
> +		if $! && $! != ENOENT;

nit: I used a single line in the follow-up as I touched the area anyway (wouldn't
have cared to much else). I also updated the style guide wiki slightly to reflect
that post-if's are most often fine to use the upper limit of 100cc text-width as
their main use is short/concise error handling anyway. But as said more of a nit
from my side.

> +
>  	    if (my $err = $@) {
>  		eval { $err_cleanup->() };
>  		warn "$@" if $@;
> 





^ permalink raw reply	[flat|nested] 17+ messages in thread

* [pve-devel] applied: [PATCH v4 http-server 1/1] anyevent: move unlink from http-server to endpoint
  2021-08-31 10:16 ` [pve-devel] [PATCH v4 http-server 1/1] anyevent: move unlink from http-server to endpoint Lorenz Stechauner
@ 2021-10-04  8:16   ` Thomas Lamprecht
  0 siblings, 0 replies; 17+ messages in thread
From: Thomas Lamprecht @ 2021-10-04  8:16 UTC (permalink / raw)
  To: Proxmox VE development discussion, Lorenz Stechauner

On 31.08.21 12:16, Lorenz Stechauner wrote:
> any uploaded file has to be deleted by the corrosponding
> endpoint. the file upload was only used by the 'upload to
> storage' feature in pve.
> 
> this change allows the endpoint to delete the file itself,
> making the old and racey`sleep 1` (waiting until the worker
> has opened the file) obsolete.
> 
> this change breaks all pve-manager versions, in which the
> worker does not unlink the temp file itself.
> 
> Signed-off-by: Lorenz Stechauner <l.stechauner@proxmox.com>
> ---
>  src/PVE/APIServer/AnyEvent.pm | 8 ++++----
>  1 file changed, 4 insertions(+), 4 deletions(-)
> 
>

applied, thanks!

But I actually now let it break libpve-storage-perl (<< 7.0-11) as that should already
avoid all situations where the old code could run.




^ permalink raw reply	[flat|nested] 17+ messages in thread

* [pve-devel] partially applied: [PATCH v4 storage 4/4] status: fix tmpfile cleanup
  2021-08-31 10:16 ` [pve-devel] [PATCH v4 storage 4/4] status: fix tmpfile cleanup Lorenz Stechauner
@ 2021-11-04 15:33   ` Thomas Lamprecht
  0 siblings, 0 replies; 17+ messages in thread
From: Thomas Lamprecht @ 2021-11-04 15:33 UTC (permalink / raw)
  To: Proxmox VE development discussion, Lorenz Stechauner

On 31.08.21 12:16, Lorenz Stechauner wrote:
> $tmpfilename already gets unlinked after executing the cmd.
> 
> furthermore, because this is a local file, it is wrong to delete
> it via the ssh command on a remote node.
> 
> small change: added \n to the error message.
> 
> Signed-off-by: Lorenz Stechauner <l.stechauner@proxmox.com>
> ---
>  PVE/API2/Storage/Status.pm | 4 ++--
>  1 file changed, 2 insertions(+), 2 deletions(-)
> 
>

applied the storage parts already a bit ago but it seems I forgot to reply
that on the list, thanks!




^ permalink raw reply	[flat|nested] 17+ messages in thread

* Re: [pve-devel] [PATCH v4 manager 2/5] ui: refactor UploadToStorage.js
  2021-08-31 10:16 ` [pve-devel] [PATCH v4 manager 2/5] ui: refactor UploadToStorage.js Lorenz Stechauner
@ 2021-11-05  8:12   ` Dominik Csapak
  0 siblings, 0 replies; 17+ messages in thread
From: Dominik Csapak @ 2021-11-05  8:12 UTC (permalink / raw)
  To: Proxmox VE development discussion, Lorenz Stechauner

comments inline

On 8/31/21 12:16, Lorenz Stechauner wrote:
> this also removes the "content" selector from the window.
> as far as it seems, this selector was never able to select
> more than one entry, so it was useless.
> 
> the check for FormData() is also removed, because this is
> supported by all major browsers for a long time. therefore
> doStandardSubmit() is also not necessary.
> 
> Signed-off-by: Lorenz Stechauner <l.stechauner@proxmox.com>
> ---
>   www/manager6/storage/ContentView.js    |   2 +-
>   www/manager6/window/UploadToStorage.js | 346 ++++++++++++++-----------
>   2 files changed, 196 insertions(+), 152 deletions(-)
> 
> diff --git a/www/manager6/storage/ContentView.js b/www/manager6/storage/ContentView.js
> index ca0ad664..00a94f3c 100644
> --- a/www/manager6/storage/ContentView.js
> +++ b/www/manager6/storage/ContentView.js
> @@ -69,7 +69,7 @@ Ext.define('PVE.storage.ContentView', {
>   			Ext.create('PVE.window.UploadToStorage', {
>   			    nodename: nodename,
>   			    storage: storage,
> -			    contents: [content],
> +			    content: content,
>   			    autoShow: true,
>   			    taskDone: () => reload(),
>   			});
> diff --git a/www/manager6/window/UploadToStorage.js b/www/manager6/window/UploadToStorage.js
> index 3c35020a..e7db41fb 100644
> --- a/www/manager6/window/UploadToStorage.js
> +++ b/www/manager6/window/UploadToStorage.js
> @@ -1,191 +1,235 @@
>   Ext.define('PVE.window.UploadToStorage', {
>       extend: 'Ext.window.Window',
>       alias: 'widget.pveStorageUpload',
> +    mixins: ['Proxmox.Mixin.CBind'],
>   
>       resizable: false,
> -
>       modal: true,
>   
> -    initComponent: function() {
> -        var me = this;
> +    title: gettext('Upload'),
>   
> -	if (!me.nodename) {
> -	    throw "no node name specified";
> -	}
> -	if (!me.storage) {
> -	    throw "no storage ID specified";
> -	}
> +    acceptedExtensions: {
> +	iso: ['.img', '.iso'],
> +	vztmpl: ['.tar.gz', '.tar.xz'],
> +    },
>   
> -	let baseurl = `/nodes/${me.nodename}/storage/${me.storage}/upload`;
> +    cbindData: function(initialConfig) {
> +	const me = this;
> +	const ext = me.acceptedExtensions[me.content];
>   
> -	let pbar = Ext.create('Ext.ProgressBar', {
> -            text: 'Ready',
> -	    hidden: true,
> -	});
> +	me.url = `/nodes/${me.nodename}/storage/${me.storage}/upload`;
>   
> -	let acceptedExtensions = {
> -	    iso: ".img, .iso",
> -	    vztmpl: ".tar.gz, .tar.xz",
> +	return {
> +	    extensions: ext.join(', '),


nit:

this join will fail if the content does not exist in the 
'acceptedExtensions' object and the check in initcomponent
will never trigger

i'd either use the resulting string directly
(".img, .iso"; probably bad because you later compute the regex from
this array) or make an empty array if it does not exists:

const ext = me.acceptedExtensions[me.content] || [];
that way the join will not fail and the initComponent check can
trigger

in reality this will probably not happen so fast, as we do
not often add/modify content types, but would still be nice
to have proper error handling here

>   	};
> +    },
>   
> -	let defaultContent = me.contents[0] || '';
> -
> -	let fileField = Ext.create('Ext.form.field.File', {
> -	    name: 'filename',
> -	    buttonText: gettext('Select File...'),
> -	    allowBlank: false,
> -	    setAccept: function(content) {
> -		let acceptString = acceptedExtensions[content] || '';
> -		this.fileInputEl.set({
> -		    accept: acceptString,
> -		});
> -	    },
> -	    listeners: {
> -		afterrender: function(cmp) {
> -		    cmp.setAccept(defaultContent);
> -		},
> -	    },
> -	});
> +    viewModel: {
> +	data: {
> +	    size: '-',
> +	    mimetype: '-',
> +	    filename: '',
> +	},
> +    },
> +
> +    controller: {
> +	submit: function(button) {
> +	    const view = this.getView();
> +	    const form = this.lookup('formPanel').getForm();
> +	    const abortBtn = this.lookup('abortBtn');
> +	    const pbar = this.lookup('progressBar');
> +
> +	    const updateProgress = function(per, bytes) {
> +		let text = (per * 100).toFixed(2) + '%';
> +		if (bytes) {
> +		    text += " (" + Proxmox.Utils.format_size(bytes) + ')';
> +		}
> +		pbar.updateProgress(per, text);
> +	    };
> +
> +	    const fd = new FormData();
> +
> +	    button.setDisabled(true);
> +	    abortBtn.setDisabled(false);
> +
> +	    fd.append("content", view.content);
>   
> -	me.formPanel = Ext.create('Ext.form.Panel', {
> +	    const fileField = form.findField('file');
> +	    const file = fileField.fileInputEl.dom.files[0];
> +	    fileField.setDisabled(true);
> +
> +	    const filenameField = form.findField('filename');
> +	    const filename = filenameField.getValue();
> +	    filenameField.setDisabled(true);
> +
> +	    fd.append("filename", file, filename);
> +
> +	    pbar.setVisible(true);
> +	    updateProgress(0);
> +
> +	    const xhr = new XMLHttpRequest();
> +	    view.xhr = xhr;
> +
> +	    xhr.addEventListener("load", function(e) {
> +		if (xhr.status === 200) {
> +		    view.close();
> +		    return;
> +		}
> +		const err = Ext.htmlEncode(xhr.statusText);
> +		let msg = `${gettext('Error')} ${xhr.status.toString()}: ${err}`;
> +		if (xhr.responseText !== "") {
> +		    const result = Ext.decode(xhr.responseText);
> +		    result.message = msg;
> +		    msg = Proxmox.Utils.extractRequestError(result, true);
> +		}
> +		Ext.Msg.alert(gettext('Error'), msg, btn => view.close());
> +	    }, false);
> +
> +	    xhr.addEventListener("error", function(e) {
> +		const err = e.target.status.toString();
> +		const msg = `Error '${err}' occurred while receiving the document.`;
> +		Ext.Msg.alert(gettext('Error'), msg, btn => view.close());
> +	    });
> +
> +	    xhr.upload.addEventListener("progress", function(evt) {
> +		if (evt.lengthComputable) {
> +		    const percentComplete = evt.loaded / evt.total;
> +		    updateProgress(percentComplete, evt.loaded);
> +		}
> +	    }, false);
> +
> +	    xhr.open("POST", `/api2/json${view.url}`, true);
> +	    xhr.send(fd);
> +	},
> +
> +	validitychange: function(f, valid) {
> +	    const submitBtn = this.lookup('submitBtn');
> +	    submitBtn.setDisabled(!valid);
> +	},
> +
> +	fileChange: function(input) {
> +	    const vm = this.getViewModel();
> +	    const name = input.value.replace(/^.*(\/|\\)/, '');
> +	    const fileInput = input.fileInputEl.dom;
> +	    vm.set('filename', name);
> +	    vm.set('size', (fileInput.files[0] && Proxmox.Utils.format_size(fileInput.files[0].size)) || '-');
> +	    vm.set('mimetype', (fileInput.files[0] && fileInput.files[0].type) || '-');
> +	},
> +    },
> +
> +    items: [
> +	{
> +	    xtype: 'form',
> +	    reference: 'formPanel',
>   	    method: 'POST',
>   	    waitMsgTarget: true,
>   	    bodyPadding: 10,
>   	    border: false,
> -	    width: 300,
> +	    width: 400,
>   	    fieldDefaults: {
>   		labelWidth: 100,
>   		anchor: '100%',
>               },
>   	    items: [
>   		{
> -		    xtype: 'pveContentTypeSelector',
> -		    cts: me.contents,
> -		    fieldLabel: gettext('Content'),
> -		    name: 'content',
> -		    value: defaultContent,
> +		    xtype: 'filefield',
> +		    name: 'file',
> +		    buttonText: gettext('Select File'),
>   		    allowBlank: false,
> +		    fieldLabel: gettext('File'),
> +		    cbind: {
> +			accept: '{extensions}',
> +		    },
>   		    listeners: {
> -			change: function(cmp, newValue, oldValue) {
> -			    fileField.setAccept(newValue);
> -			},
> +			change: 'fileChange',
>   		    },
>   		},
> -		fileField,
> -		pbar,
> -	    ],
> -	});
> -
> -	let form = me.formPanel.getForm();
> -
> -	let doStandardSubmit = function() {
> -	    form.submit({
> -		url: "/api2/htmljs" + baseurl,
> -		waitMsg: gettext('Uploading file...'),
> -		success: function(f, action) {
> -		    me.close();
> +		{
> +		    xtype: 'textfield',
> +		    name: 'filename',
> +		    allowBlank: false,
> +		    fieldLabel: gettext('File name'),
> +		    bind: {
> +			value: '{filename}',
> +		    },
>   		},
> -		failure: function(f, action) {
> -		    var msg = PVE.Utils.extractFormActionError(action);
> -                    Ext.Msg.alert(gettext('Error'), msg);
> +		{
> +		    xtype: 'displayfield',
> +		    name: 'size',
> +		    fieldLabel: gettext('File size'),
> +		    bind: {
> +			value: '{size}',
> +		    },
>   		},
> -	    });
> -	};
> -
> -	let updateProgress = function(per, bytes) {
> -	    var text = (per * 100).toFixed(2) + '%';
> -	    if (bytes) {
> -		text += " (" + Proxmox.Utils.format_size(bytes) + ')';
> -	    }
> -	    pbar.updateProgress(per, text);
> -	};
> -
> -	let abortBtn = Ext.create('Ext.Button', {
> +		{
> +		    xtype: 'displayfield',
> +		    name: 'mimetype',
> +		    fieldLabel: gettext('MIME type'),
> +		    bind: {
> +			value: '{mimetype}',
> +		    },
> +		},
> +		{
> +		    xtype: 'progressbar',
> +		    text: 'Ready',
> +		    hidden: true,
> +		    reference: 'progressBar',
> +		},
> +		{
> +		    xtype: 'hiddenfield',
> +		    name: 'content',
> +		    cbind: {
> +			value: '{content}',
> +		    },
> +		},
> +	    ],
> +	   listeners: {
> +		validitychange: 'validitychange',
> +	   },
> +	},
> +    ],
> +
> +    buttons: [
> +	{
> +	    xtype: 'button',
>   	    text: gettext('Abort'),
> +	    reference: 'abortBtn',
>   	    disabled: true,
>   	    handler: function() {
> -		me.close();
> +		const me = this;
> +		me.up('pveStorageUpload').close();
>   	    },
> -	});
> -
> -	let submitBtn = Ext.create('Ext.Button', {
> +	},
> +	{
>   	    text: gettext('Upload'),
> +	    reference: 'submitBtn',
>   	    disabled: true,
> -	    handler: function(button) {
> -		var fd;
> -		try {
> -		    fd = new FormData();
> -		} catch (err) {
> -		    doStandardSubmit();
> -		    return;
> -		}
> +	    handler: 'submit',
> +	},
> +    ],
> +
> +    listeners: {
> +	close: function() {
> +	    const me = this;
> +	    if (me.xhr) {
> +		me.xhr.abort();
> +		delete me.xhr;
> +	    }
> +	},
> +    },
>   
> -		button.setDisabled(true);
> -		abortBtn.setDisabled(false);
> -
> -		var field = form.findField('content');
> -		fd.append("content", field.getValue());
> -		field.setDisabled(true);
> -
> -		field = form.findField('filename');
> -		var file = field.fileInputEl.dom;
> -		fd.append("filename", file.files[0]);
> -		field.setDisabled(true);
> -
> -		pbar.setVisible(true);
> -		updateProgress(0);
> -
> -		let xhr = new XMLHttpRequest();
> -		me.xhr = xhr;
> -
> -		xhr.addEventListener("load", function(e) {
> -		    if (xhr.status === 200) {
> -			me.close();
> -			return;
> -		    }
> -		    let err = Ext.htmlEncode(xhr.statusText);
> -		    let msg = `${gettext('Error')} ${xhr.status.toString()}: ${err}`;
> -		    if (xhr.responseText !== "") {
> -			let result = Ext.decode(xhr.responseText);
> -			result.message = msg;
> -			msg = Proxmox.Utils.extractRequestError(result, true);
> -		    }
> -		    Ext.Msg.alert(gettext('Error'), msg, btn => me.close());
> -		}, false);
> -
> -		xhr.addEventListener("error", function(e) {
> -		    let err = e.target.status.toString();
> -		    let msg = `Error '${err}' occurred while receiving the document.`;
> -		    Ext.Msg.alert(gettext('Error'), msg, btn => me.close());
> -		});
> -
> -		xhr.upload.addEventListener("progress", function(evt) {
> -		    if (evt.lengthComputable) {
> -			let percentComplete = evt.loaded / evt.total;
> -			updateProgress(percentComplete, evt.loaded);
> -		    }
> -		}, false);
> -
> -		xhr.open("POST", `/api2/json${baseurl}`, true);
> -		xhr.send(fd);
> -	    },
> -	});
> -
> -	form.on('validitychange', (f, valid) => submitBtn.setDisabled(!valid));
> -
> -	Ext.apply(me, {
> -	    title: gettext('Upload'),
> -	    items: me.formPanel,
> -	    buttons: [abortBtn, submitBtn],
> -	    listeners: {
> -		close: function() {
> -		    if (me.xhr) {
> -			me.xhr.abort();
> -			delete me.xhr;
> -		    }
> -		},
> -	    },
> -	});
> +    initComponent: function() {
> +        const me = this;
> +
> +	if (!me.nodename) {
> +	    throw "no node name specified";
> +	}
> +	if (!me.storage) {
> +	    throw "no storage ID specified";
> +	}
> +	if (!(me.content in me.acceptedExtensions)) {
> +	    throw "content type not supported";
> +	}

nit: you could make that check simpler:

if (!me.acceptedExtensions[me.content])

>   
>           me.callParent();
>       },
> 





^ permalink raw reply	[flat|nested] 17+ messages in thread

* Re: [pve-devel] [PATCH v4 manager 5/5] ui/UplaodToStorage: check file extension
  2021-08-31 10:16 ` [pve-devel] [PATCH v4 manager 5/5] ui/UplaodToStorage: check file extension Lorenz Stechauner
@ 2021-11-05  8:12   ` Dominik Csapak
  0 siblings, 0 replies; 17+ messages in thread
From: Dominik Csapak @ 2021-11-05  8:12 UTC (permalink / raw)
  To: Proxmox VE development discussion, Lorenz Stechauner

one nit inline

On 8/31/21 12:16, Lorenz Stechauner wrote:
> Signed-off-by: Lorenz Stechauner <l.stechauner@proxmox.com>
> ---
>   www/manager6/window/UploadToStorage.js | 5 +++++
>   1 file changed, 5 insertions(+)
> 
> diff --git a/www/manager6/window/UploadToStorage.js b/www/manager6/window/UploadToStorage.js
> index f0c6186f..ec83c581 100644
> --- a/www/manager6/window/UploadToStorage.js
> +++ b/www/manager6/window/UploadToStorage.js
> @@ -21,6 +21,7 @@ Ext.define('PVE.window.UploadToStorage', {
>   
>   	return {
>   	    extensions: ext.join(', '),
> +	    filenameRegex: RegExp('^.*(?:' + ext.join('|').replaceAll('.', '\\.') + ')$', 'i'),
>   	};
>       },
>   
> @@ -186,6 +187,10 @@ Ext.define('PVE.window.UploadToStorage', {
>   		    bind: {
>   			value: '{filename}',
>   		    },
> +		    cbind: {
> +			regex: '{filenameRegex}',
> +		    },
> +		    regexText: "wrong file extension",

i'd put the errortext into a gettext

>   		},
>   		{
>   		    xtype: 'displayfield',
> 





^ permalink raw reply	[flat|nested] 17+ messages in thread

* Re: [pve-devel] [PATCH v4 manager 1/5] ui: move upload window into UploadToStorage.js
  2021-08-31 10:16 ` [pve-devel] [PATCH v4 manager 1/5] ui: move upload window into UploadToStorage.js Lorenz Stechauner
@ 2021-11-05  8:12   ` Dominik Csapak
  0 siblings, 0 replies; 17+ messages in thread
From: Dominik Csapak @ 2021-11-05  8:12 UTC (permalink / raw)
  To: Proxmox VE development discussion, Lorenz Stechauner

Looks mostly ok, some small nits (replied to the separate patches)
those could also be sent as follow ups

Aside from those things

Reviewed-by: Dominik Csapak <d.csapak@proxmox.com>
Tested-by: Dominik Csapak <d.csapak@proxmox.com>


On 8/31/21 12:16, Lorenz Stechauner wrote:
> Signed-off-by: Lorenz Stechauner <l.stechauner@proxmox.com>
> ---
>   www/manager6/Makefile                  |   1 +
>   www/manager6/storage/ContentView.js    | 195 +------------------------
>   www/manager6/window/UploadToStorage.js | 192 ++++++++++++++++++++++++
>   3 files changed, 194 insertions(+), 194 deletions(-)
>   create mode 100644 www/manager6/window/UploadToStorage.js
> 
> diff --git a/www/manager6/Makefile b/www/manager6/Makefile
> index 75d355a5..552e842b 100644
> --- a/www/manager6/Makefile
> +++ b/www/manager6/Makefile
> @@ -106,6 +106,7 @@ JSSRC= 							\
>   	window/Snapshot.js				\
>   	window/StartupEdit.js				\
>   	window/DownloadUrlToStorage.js 			\
> +	window/UploadToStorage.js 			\
>   	window/Wizard.js				\
>   	ha/Fencing.js					\
>   	ha/GroupEdit.js					\
> diff --git a/www/manager6/storage/ContentView.js b/www/manager6/storage/ContentView.js
> index 3f5b686b..ca0ad664 100644
> --- a/www/manager6/storage/ContentView.js
> +++ b/www/manager6/storage/ContentView.js
> @@ -1,196 +1,3 @@
> -Ext.define('PVE.storage.Upload', {
> -    extend: 'Ext.window.Window',
> -    alias: 'widget.pveStorageUpload',
> -
> -    resizable: false,
> -
> -    modal: true,
> -
> -    initComponent: function() {
> -        var me = this;
> -
> -	if (!me.nodename) {
> -	    throw "no node name specified";
> -	}
> -	if (!me.storage) {
> -	    throw "no storage ID specified";
> -	}
> -
> -	let baseurl = `/nodes/${me.nodename}/storage/${me.storage}/upload`;
> -
> -	let pbar = Ext.create('Ext.ProgressBar', {
> -            text: 'Ready',
> -	    hidden: true,
> -	});
> -
> -	let acceptedExtensions = {
> -	    iso: ".img, .iso",
> -	    vztmpl: ".tar.gz, .tar.xz",
> -	};
> -
> -	let defaultContent = me.contents[0] || '';
> -
> -	let fileField = Ext.create('Ext.form.field.File', {
> -	    name: 'filename',
> -	    buttonText: gettext('Select File...'),
> -	    allowBlank: false,
> -	    setAccept: function(content) {
> -		let acceptString = acceptedExtensions[content] || '';
> -		this.fileInputEl.set({
> -		    accept: acceptString,
> -		});
> -	    },
> -	    listeners: {
> -		afterrender: function(cmp) {
> -		    cmp.setAccept(defaultContent);
> -		},
> -	    },
> -	});
> -
> -	me.formPanel = Ext.create('Ext.form.Panel', {
> -	    method: 'POST',
> -	    waitMsgTarget: true,
> -	    bodyPadding: 10,
> -	    border: false,
> -	    width: 300,
> -	    fieldDefaults: {
> -		labelWidth: 100,
> -		anchor: '100%',
> -            },
> -	    items: [
> -		{
> -		    xtype: 'pveContentTypeSelector',
> -		    cts: me.contents,
> -		    fieldLabel: gettext('Content'),
> -		    name: 'content',
> -		    value: defaultContent,
> -		    allowBlank: false,
> -		    listeners: {
> -			change: function(cmp, newValue, oldValue) {
> -			    fileField.setAccept(newValue);
> -			},
> -		    },
> -		},
> -		fileField,
> -		pbar,
> -	    ],
> -	});
> -
> -	let form = me.formPanel.getForm();
> -
> -	let doStandardSubmit = function() {
> -	    form.submit({
> -		url: "/api2/htmljs" + baseurl,
> -		waitMsg: gettext('Uploading file...'),
> -		success: function(f, action) {
> -		    me.close();
> -		},
> -		failure: function(f, action) {
> -		    var msg = PVE.Utils.extractFormActionError(action);
> -                    Ext.Msg.alert(gettext('Error'), msg);
> -		},
> -	    });
> -	};
> -
> -	let updateProgress = function(per, bytes) {
> -	    var text = (per * 100).toFixed(2) + '%';
> -	    if (bytes) {
> -		text += " (" + Proxmox.Utils.format_size(bytes) + ')';
> -	    }
> -	    pbar.updateProgress(per, text);
> -	};
> -
> -	let abortBtn = Ext.create('Ext.Button', {
> -	    text: gettext('Abort'),
> -	    disabled: true,
> -	    handler: function() {
> -		me.close();
> -	    },
> -	});
> -
> -	let submitBtn = Ext.create('Ext.Button', {
> -	    text: gettext('Upload'),
> -	    disabled: true,
> -	    handler: function(button) {
> -		var fd;
> -		try {
> -		    fd = new FormData();
> -		} catch (err) {
> -		    doStandardSubmit();
> -		    return;
> -		}
> -
> -		button.setDisabled(true);
> -		abortBtn.setDisabled(false);
> -
> -		var field = form.findField('content');
> -		fd.append("content", field.getValue());
> -		field.setDisabled(true);
> -
> -		field = form.findField('filename');
> -		var file = field.fileInputEl.dom;
> -		fd.append("filename", file.files[0]);
> -		field.setDisabled(true);
> -
> -		pbar.setVisible(true);
> -		updateProgress(0);
> -
> -		let xhr = new XMLHttpRequest();
> -		me.xhr = xhr;
> -
> -		xhr.addEventListener("load", function(e) {
> -		    if (xhr.status === 200) {
> -			me.close();
> -			return;
> -		    }
> -		    let err = Ext.htmlEncode(xhr.statusText);
> -		    let msg = `${gettext('Error')} ${xhr.status.toString()}: ${err}`;
> -		    if (xhr.responseText !== "") {
> -			let result = Ext.decode(xhr.responseText);
> -			result.message = msg;
> -			msg = Proxmox.Utils.extractRequestError(result, true);
> -		    }
> -		    Ext.Msg.alert(gettext('Error'), msg, btn => me.close());
> -		}, false);
> -
> -		xhr.addEventListener("error", function(e) {
> -		    let err = e.target.status.toString();
> -		    let msg = `Error '${err}' occurred while receiving the document.`;
> -		    Ext.Msg.alert(gettext('Error'), msg, btn => me.close());
> -		});
> -
> -		xhr.upload.addEventListener("progress", function(evt) {
> -		    if (evt.lengthComputable) {
> -			let percentComplete = evt.loaded / evt.total;
> -			updateProgress(percentComplete, evt.loaded);
> -		    }
> -		}, false);
> -
> -		xhr.open("POST", `/api2/json${baseurl}`, true);
> -		xhr.send(fd);
> -	    },
> -	});
> -
> -	form.on('validitychange', (f, valid) => submitBtn.setDisabled(!valid));
> -
> -	Ext.apply(me, {
> -	    title: gettext('Upload'),
> -	    items: me.formPanel,
> -	    buttons: [abortBtn, submitBtn],
> -	    listeners: {
> -		close: function() {
> -		    if (me.xhr) {
> -			me.xhr.abort();
> -			delete me.xhr;
> -		    }
> -		},
> -	    },
> -	});
> -
> -        me.callParent();
> -    },
> -});
> -
>   Ext.define('PVE.storage.ContentView', {
>       extend: 'Ext.grid.GridPanel',
>   
> @@ -259,7 +66,7 @@ Ext.define('PVE.storage.ContentView', {
>   		    text: gettext('Upload'),
>   		    disabled: !me.enableUploadButton,
>   		    handler: function() {
> -			Ext.create('PVE.storage.Upload', {
> +			Ext.create('PVE.window.UploadToStorage', {
>   			    nodename: nodename,
>   			    storage: storage,
>   			    contents: [content],
> diff --git a/www/manager6/window/UploadToStorage.js b/www/manager6/window/UploadToStorage.js
> new file mode 100644
> index 00000000..3c35020a
> --- /dev/null
> +++ b/www/manager6/window/UploadToStorage.js
> @@ -0,0 +1,192 @@
> +Ext.define('PVE.window.UploadToStorage', {
> +    extend: 'Ext.window.Window',
> +    alias: 'widget.pveStorageUpload',
> +
> +    resizable: false,
> +
> +    modal: true,
> +
> +    initComponent: function() {
> +        var me = this;
> +
> +	if (!me.nodename) {
> +	    throw "no node name specified";
> +	}
> +	if (!me.storage) {
> +	    throw "no storage ID specified";
> +	}
> +
> +	let baseurl = `/nodes/${me.nodename}/storage/${me.storage}/upload`;
> +
> +	let pbar = Ext.create('Ext.ProgressBar', {
> +            text: 'Ready',
> +	    hidden: true,
> +	});
> +
> +	let acceptedExtensions = {
> +	    iso: ".img, .iso",
> +	    vztmpl: ".tar.gz, .tar.xz",
> +	};
> +
> +	let defaultContent = me.contents[0] || '';
> +
> +	let fileField = Ext.create('Ext.form.field.File', {
> +	    name: 'filename',
> +	    buttonText: gettext('Select File...'),
> +	    allowBlank: false,
> +	    setAccept: function(content) {
> +		let acceptString = acceptedExtensions[content] || '';
> +		this.fileInputEl.set({
> +		    accept: acceptString,
> +		});
> +	    },
> +	    listeners: {
> +		afterrender: function(cmp) {
> +		    cmp.setAccept(defaultContent);
> +		},
> +	    },
> +	});
> +
> +	me.formPanel = Ext.create('Ext.form.Panel', {
> +	    method: 'POST',
> +	    waitMsgTarget: true,
> +	    bodyPadding: 10,
> +	    border: false,
> +	    width: 300,
> +	    fieldDefaults: {
> +		labelWidth: 100,
> +		anchor: '100%',
> +            },
> +	    items: [
> +		{
> +		    xtype: 'pveContentTypeSelector',
> +		    cts: me.contents,
> +		    fieldLabel: gettext('Content'),
> +		    name: 'content',
> +		    value: defaultContent,
> +		    allowBlank: false,
> +		    listeners: {
> +			change: function(cmp, newValue, oldValue) {
> +			    fileField.setAccept(newValue);
> +			},
> +		    },
> +		},
> +		fileField,
> +		pbar,
> +	    ],
> +	});
> +
> +	let form = me.formPanel.getForm();
> +
> +	let doStandardSubmit = function() {
> +	    form.submit({
> +		url: "/api2/htmljs" + baseurl,
> +		waitMsg: gettext('Uploading file...'),
> +		success: function(f, action) {
> +		    me.close();
> +		},
> +		failure: function(f, action) {
> +		    var msg = PVE.Utils.extractFormActionError(action);
> +                    Ext.Msg.alert(gettext('Error'), msg);
> +		},
> +	    });
> +	};
> +
> +	let updateProgress = function(per, bytes) {
> +	    var text = (per * 100).toFixed(2) + '%';
> +	    if (bytes) {
> +		text += " (" + Proxmox.Utils.format_size(bytes) + ')';
> +	    }
> +	    pbar.updateProgress(per, text);
> +	};
> +
> +	let abortBtn = Ext.create('Ext.Button', {
> +	    text: gettext('Abort'),
> +	    disabled: true,
> +	    handler: function() {
> +		me.close();
> +	    },
> +	});
> +
> +	let submitBtn = Ext.create('Ext.Button', {
> +	    text: gettext('Upload'),
> +	    disabled: true,
> +	    handler: function(button) {
> +		var fd;
> +		try {
> +		    fd = new FormData();
> +		} catch (err) {
> +		    doStandardSubmit();
> +		    return;
> +		}
> +
> +		button.setDisabled(true);
> +		abortBtn.setDisabled(false);
> +
> +		var field = form.findField('content');
> +		fd.append("content", field.getValue());
> +		field.setDisabled(true);
> +
> +		field = form.findField('filename');
> +		var file = field.fileInputEl.dom;
> +		fd.append("filename", file.files[0]);
> +		field.setDisabled(true);
> +
> +		pbar.setVisible(true);
> +		updateProgress(0);
> +
> +		let xhr = new XMLHttpRequest();
> +		me.xhr = xhr;
> +
> +		xhr.addEventListener("load", function(e) {
> +		    if (xhr.status === 200) {
> +			me.close();
> +			return;
> +		    }
> +		    let err = Ext.htmlEncode(xhr.statusText);
> +		    let msg = `${gettext('Error')} ${xhr.status.toString()}: ${err}`;
> +		    if (xhr.responseText !== "") {
> +			let result = Ext.decode(xhr.responseText);
> +			result.message = msg;
> +			msg = Proxmox.Utils.extractRequestError(result, true);
> +		    }
> +		    Ext.Msg.alert(gettext('Error'), msg, btn => me.close());
> +		}, false);
> +
> +		xhr.addEventListener("error", function(e) {
> +		    let err = e.target.status.toString();
> +		    let msg = `Error '${err}' occurred while receiving the document.`;
> +		    Ext.Msg.alert(gettext('Error'), msg, btn => me.close());
> +		});
> +
> +		xhr.upload.addEventListener("progress", function(evt) {
> +		    if (evt.lengthComputable) {
> +			let percentComplete = evt.loaded / evt.total;
> +			updateProgress(percentComplete, evt.loaded);
> +		    }
> +		}, false);
> +
> +		xhr.open("POST", `/api2/json${baseurl}`, true);
> +		xhr.send(fd);
> +	    },
> +	});
> +
> +	form.on('validitychange', (f, valid) => submitBtn.setDisabled(!valid));
> +
> +	Ext.apply(me, {
> +	    title: gettext('Upload'),
> +	    items: me.formPanel,
> +	    buttons: [abortBtn, submitBtn],
> +	    listeners: {
> +		close: function() {
> +		    if (me.xhr) {
> +			me.xhr.abort();
> +			delete me.xhr;
> +		    }
> +		},
> +	    },
> +	});
> +
> +        me.callParent();
> +    },
> +});
> 





^ permalink raw reply	[flat|nested] 17+ messages in thread

end of thread, other threads:[~2021-11-05  8:12 UTC | newest]

Thread overview: 17+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2021-08-31 10:16 [pve-devel] [PATCH-SERIES v4 http-server/storage/manager] fix #3505: add checksum and algorithm to iso upload Lorenz Stechauner
2021-08-31 10:16 ` [pve-devel] [PATCH v4 http-server 1/1] anyevent: move unlink from http-server to endpoint Lorenz Stechauner
2021-10-04  8:16   ` [pve-devel] applied: " Thomas Lamprecht
2021-08-31 10:16 ` [pve-devel] [PATCH v4 storage 1/4] status: move unlink from http-server to enpoint Lorenz Stechauner
2021-09-06  7:04   ` [pve-devel] applied: " Thomas Lamprecht
2021-08-31 10:16 ` [pve-devel] [PATCH v4 storage 2/4] status: remove sleep(1) in file upload Lorenz Stechauner
2021-08-31 10:16 ` [pve-devel] [PATCH v4 storage 3/4] fix #3505: status: add checksum and algorithm to " Lorenz Stechauner
2021-08-31 10:16 ` [pve-devel] [PATCH v4 storage 4/4] status: fix tmpfile cleanup Lorenz Stechauner
2021-11-04 15:33   ` [pve-devel] partially applied: " Thomas Lamprecht
2021-08-31 10:16 ` [pve-devel] [PATCH v4 manager 1/5] ui: move upload window into UploadToStorage.js Lorenz Stechauner
2021-11-05  8:12   ` Dominik Csapak
2021-08-31 10:16 ` [pve-devel] [PATCH v4 manager 2/5] ui: refactor UploadToStorage.js Lorenz Stechauner
2021-11-05  8:12   ` Dominik Csapak
2021-08-31 10:16 ` [pve-devel] [PATCH v4 manager 3/5] fix #3505: ui/UploadToStorage: add checksum and algorithm Lorenz Stechauner
2021-08-31 10:16 ` [pve-devel] [PATCH v4 manager 4/5] ui/UploadToStorage: add TaskViewer Lorenz Stechauner
2021-08-31 10:16 ` [pve-devel] [PATCH v4 manager 5/5] ui/UplaodToStorage: check file extension Lorenz Stechauner
2021-11-05  8:12   ` Dominik Csapak

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox
Service provided by Proxmox Server Solutions GmbH | Privacy | Legal