mirror of
https://github.com/45Drives/cockpit-navigator.git
synced 2025-07-30 00:55:30 +02:00
Merge branch 'dev-josh'
This commit is contained in:
commit
f6a389e4f8
@ -1,4 +1,4 @@
|
|||||||
## Cockpit Navigator 0.5.1-1
|
## Cockpit Navigator 0.5.2-1
|
||||||
|
|
||||||
* Allow modal popups to scroll if overflowing past page.
|
* Implement uploading of entire directories.
|
||||||
* Moves focus to next input in modal popup when enter is pressed.
|
* Add cancel option to in-progress file uploads.
|
10
README.md
10
README.md
@ -23,17 +23,17 @@ With no command line use needed, you can:
|
|||||||
# Installation
|
# Installation
|
||||||
## From Github Release
|
## From Github Release
|
||||||
### Ubuntu
|
### Ubuntu
|
||||||
1. `$ wget https://github.com/45Drives/cockpit-navigator/releases/download/v0.5.1/cockpit-navigator_0.5.1-1focal_all.deb`
|
1. `$ wget https://github.com/45Drives/cockpit-navigator/releases/download/v0.5.2/cockpit-navigator_0.5.2-1focal_all.deb`
|
||||||
1. `# apt install ./cockpit-navigator_0.5.1-1focal_all.deb`
|
1. `# apt install ./cockpit-navigator_0.5.2-1focal_all.deb`
|
||||||
### EL7
|
### EL7
|
||||||
1. `# yum install https://github.com/45Drives/cockpit-navigator/releases/download/v0.5.1/cockpit-navigator-0.5.1-1.el7.noarch.rpm`
|
1. `# yum install https://github.com/45Drives/cockpit-navigator/releases/download/v0.5.2/cockpit-navigator-0.5.2-1.el7.noarch.rpm`
|
||||||
### EL8
|
### EL8
|
||||||
1. `# dnf install https://github.com/45Drives/cockpit-navigator/releases/download/v0.5.1/cockpit-navigator-0.5.1-1.el8.noarch.rpm`
|
1. `# dnf install https://github.com/45Drives/cockpit-navigator/releases/download/v0.5.2/cockpit-navigator-0.5.2-1.el8.noarch.rpm`
|
||||||
## From Source
|
## From Source
|
||||||
1. Ensure dependencies are installed: `cockpit`, `python3`, `rsync`, `zip`.
|
1. Ensure dependencies are installed: `cockpit`, `python3`, `rsync`, `zip`.
|
||||||
1. `$ git clone https://github.com/45Drives/cockpit-navigator.git`
|
1. `$ git clone https://github.com/45Drives/cockpit-navigator.git`
|
||||||
1. `$ cd cockpit-navigator`
|
1. `$ cd cockpit-navigator`
|
||||||
1. `$ git checkout <version>` (v0.5.1 is latest)
|
1. `$ git checkout <version>` (v0.5.2 is latest)
|
||||||
1. `# make install`
|
1. `# make install`
|
||||||
## From 45Drives Repositories
|
## From 45Drives Repositories
|
||||||
### Ubuntu
|
### Ubuntu
|
||||||
|
@ -3,7 +3,7 @@
|
|||||||
"name": "cockpit-navigator",
|
"name": "cockpit-navigator",
|
||||||
"title": "Cockpit Navigator",
|
"title": "Cockpit Navigator",
|
||||||
"prerelease": false,
|
"prerelease": false,
|
||||||
"version": "0.5.1",
|
"version": "0.5.2",
|
||||||
"buildVersion": "1",
|
"buildVersion": "1",
|
||||||
"author": "Josh Boudreau <jboudreau@45drives.com>",
|
"author": "Josh Boudreau <jboudreau@45drives.com>",
|
||||||
"url": "https://github.com/45Drives/cockpit-navigator",
|
"url": "https://github.com/45Drives/cockpit-navigator",
|
||||||
@ -54,7 +54,7 @@
|
|||||||
],
|
],
|
||||||
"changelog": {
|
"changelog": {
|
||||||
"urgency": "medium",
|
"urgency": "medium",
|
||||||
"version": "0.5.1",
|
"version": "0.5.2",
|
||||||
"buildVersion": "1",
|
"buildVersion": "1",
|
||||||
"ignore": [],
|
"ignore": [],
|
||||||
"date": null,
|
"date": null,
|
||||||
|
@ -19,27 +19,31 @@
|
|||||||
|
|
||||||
import {NavWindow} from "./NavWindow.js";
|
import {NavWindow} from "./NavWindow.js";
|
||||||
import {format_time_remaining} from "../functions.js";
|
import {format_time_remaining} from "../functions.js";
|
||||||
|
import {ModalPrompt} from "./ModalPrompt.js";
|
||||||
|
|
||||||
export class FileUpload {
|
export class FileUpload {
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
* @param {File|Blob} file
|
* @param {File|Blob} file
|
||||||
* @param {NavWindow} nav_window_ref
|
* @param {NavWindow} nav_window_ref
|
||||||
|
* @param {string|undefined} path_prefix
|
||||||
*/
|
*/
|
||||||
constructor(file, nav_window_ref) {
|
constructor(file, nav_window_ref, path_prefix = "") {
|
||||||
try {
|
try {
|
||||||
this.chunk_size = (parseInt(cockpit.info.version) > 238)? 1048576 : 65536;
|
this.chunk_size = (parseInt(cockpit.info.version) > 238)? 1048576 : 65536;
|
||||||
} catch(e) {
|
} catch(e) {
|
||||||
console.log(e);
|
console.log(e);
|
||||||
this.chunk_size = 65536;
|
this.chunk_size = 65536;
|
||||||
}
|
}
|
||||||
this.filename = file.name;
|
this.filename = path_prefix + file.name;
|
||||||
this.nav_window_ref = nav_window_ref;
|
this.nav_window_ref = nav_window_ref;
|
||||||
this.path = nav_window_ref.pwd().path_str() + "/" + file.name;
|
this.path = nav_window_ref.pwd().path_str() + "/" + this.filename;
|
||||||
this.reader = new FileReader();
|
this.reader = new FileReader();
|
||||||
this.chunks = this.slice_file(file);
|
this.chunks = this.slice_file(file);
|
||||||
this.chunk_index = 0;
|
this.chunk_index = 0;
|
||||||
this.timestamp = Date.now();
|
this.timestamp = Date.now();
|
||||||
|
this.modal_prompt = new ModalPrompt();
|
||||||
|
this.using_webkit = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
check_if_exists() {
|
check_if_exists() {
|
||||||
@ -58,6 +62,21 @@ export class FileUpload {
|
|||||||
header.classList.add("nav-notification-header");
|
header.classList.add("nav-notification-header");
|
||||||
notification.appendChild(header);
|
notification.appendChild(header);
|
||||||
header.innerText = "Uploading " + this.filename;
|
header.innerText = "Uploading " + this.filename;
|
||||||
|
header.style.position = "relative";
|
||||||
|
header.style.paddingRight = "1em";
|
||||||
|
|
||||||
|
var cancel = document.createElement("i");
|
||||||
|
cancel.classList.add("fa", "fa-times");
|
||||||
|
cancel.style.position = "absolute"
|
||||||
|
cancel.style.right = "0";
|
||||||
|
cancel.style.cursor = "pointer";
|
||||||
|
cancel.onclick = () => {
|
||||||
|
if (this.proc) {
|
||||||
|
this.reader.onload = () => {};
|
||||||
|
this.done();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
header.appendChild(cancel);
|
||||||
|
|
||||||
var info = document.createElement("div");
|
var info = document.createElement("div");
|
||||||
info.classList.add("flex-row", "space-between");
|
info.classList.add("flex-row", "space-between");
|
||||||
@ -94,7 +113,7 @@ export class FileUpload {
|
|||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
* @param {File|Blob} file
|
* @param {File|Blob} file
|
||||||
* @returns {Array}
|
* @returns {Blob[]}
|
||||||
*/
|
*/
|
||||||
slice_file(file) {
|
slice_file(file) {
|
||||||
var offset = 0;
|
var offset = 0;
|
||||||
@ -110,35 +129,48 @@ export class FileUpload {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async upload() {
|
async upload() {
|
||||||
if (await this.check_if_exists()) {
|
|
||||||
if (!await this.nav_window_ref.modal_prompt.confirm(this.filename + ": File exists. Replace?", "", true))
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
this.make_html_element();
|
this.make_html_element();
|
||||||
this.proc = cockpit.spawn(["/usr/share/cockpit/navigator/scripts/write-chunks.py3", this.path], {err: "out", superuser: "try"});
|
this.proc = cockpit.spawn(["/usr/share/cockpit/navigator/scripts/write-chunks.py3", this.path], {err: "out", superuser: "try"});
|
||||||
this.proc.fail((e, data) => {
|
this.proc.fail((e, data) => {
|
||||||
this.reader.onload = () => {}
|
this.reader.onload = () => {}
|
||||||
this.done();
|
this.done();
|
||||||
this.nav_window_ref.modal_prompt.alert(data);
|
this.nav_window_ref.modal_prompt.alert(e, data);
|
||||||
})
|
})
|
||||||
this.proc.done((data) => {
|
this.proc.done((data) => {
|
||||||
this.nav_window_ref.refresh();
|
this.nav_window_ref.refresh();
|
||||||
})
|
})
|
||||||
this.reader.onload = (function(uploader_ref) {
|
this.reader.onerror = (evt) => {
|
||||||
return async function(evt) {
|
this.modal_prompt.alert("Failed to read file: " + this.filename, "Upload of directories not supported.");
|
||||||
uploader_ref.write_to_file(evt, uploader_ref.chunk_index * uploader_ref.chunk_size);
|
this.done();
|
||||||
uploader_ref.chunk_index++;
|
}
|
||||||
uploader_ref.progress.value = uploader_ref.chunk_index;
|
this.reader.onload = (evt) => {
|
||||||
if (uploader_ref.chunk_index < uploader_ref.num_chunks)
|
this.write_to_file(evt, this.chunk_index * this.chunk_size);
|
||||||
uploader_ref.reader.readAsArrayBuffer(uploader_ref.chunks[uploader_ref.chunk_index]);
|
this.chunk_index++;
|
||||||
else {
|
this.progress.value = this.chunk_index;
|
||||||
uploader_ref.done();
|
if (this.chunk_index < this.num_chunks)
|
||||||
}
|
this.reader.readAsArrayBuffer(this.chunks[this.chunk_index]);
|
||||||
};
|
else {
|
||||||
})(this);
|
this.done();
|
||||||
this.reader.readAsArrayBuffer(this.chunks[0]);
|
}
|
||||||
|
};
|
||||||
|
try {
|
||||||
|
this.reader.readAsArrayBuffer(this.chunks[0]);
|
||||||
|
} catch {
|
||||||
|
this.reader.onload = () => {};
|
||||||
|
if (this.using_webkit) {
|
||||||
|
this.proc.input(JSON.stringify({seek: 0, chunk: ""}), true);
|
||||||
|
} else {
|
||||||
|
this.modal_prompt.alert("Failed to read file: " + this.filename, "Upload of directories and empty files not supported.");
|
||||||
|
}
|
||||||
|
this.done();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @param {ArrayBuffer} buffer
|
||||||
|
* @returns
|
||||||
|
*/
|
||||||
arrayBufferToBase64(buffer) {
|
arrayBufferToBase64(buffer) {
|
||||||
let binary = '';
|
let binary = '';
|
||||||
let bytes = new Uint8Array(buffer);
|
let bytes = new Uint8Array(buffer);
|
||||||
|
@ -111,6 +111,7 @@ export class ModalPrompt {
|
|||||||
this.footer.innerHTML = "";
|
this.footer.innerHTML = "";
|
||||||
this.footer.appendChild(this.ok);
|
this.footer.appendChild(this.ok);
|
||||||
this.show();
|
this.show();
|
||||||
|
this.ok.focus();
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
this.ok.onclick = () => {
|
this.ok.onclick = () => {
|
||||||
resolve();
|
resolve();
|
||||||
@ -137,6 +138,10 @@ export class ModalPrompt {
|
|||||||
else
|
else
|
||||||
this.yes.classList.add(primary_btn);
|
this.yes.classList.add(primary_btn);
|
||||||
this.show();
|
this.show();
|
||||||
|
if (danger)
|
||||||
|
this.no.focus();
|
||||||
|
else
|
||||||
|
this.yes.focus();
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
let resolve_true = () => {
|
let resolve_true = () => {
|
||||||
resolve(true);
|
resolve(true);
|
||||||
@ -179,7 +184,7 @@ export class ModalPrompt {
|
|||||||
let label = document.createElement("label");
|
let label = document.createElement("label");
|
||||||
label.innerText = request.label;
|
label.innerText = request.label;
|
||||||
label.htmlFor = key;
|
label.htmlFor = key;
|
||||||
label.style.marginRight = "1em";
|
label.style.paddingRight = "1em";
|
||||||
label.style.flexBasis = "0";
|
label.style.flexBasis = "0";
|
||||||
label.style.flexGrow = "1";
|
label.style.flexGrow = "1";
|
||||||
let req = document.createElement("input");
|
let req = document.createElement("input");
|
||||||
@ -196,6 +201,9 @@ export class ModalPrompt {
|
|||||||
case "text":
|
case "text":
|
||||||
req.style.flexGrow = "3";
|
req.style.flexGrow = "3";
|
||||||
break;
|
break;
|
||||||
|
case "checkbox":
|
||||||
|
label.style.cursor = req.style.cursor = "pointer";
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -18,6 +18,7 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
import {FileUpload} from "./FileUpload.js";
|
import {FileUpload} from "./FileUpload.js";
|
||||||
|
import { ModalPrompt } from "./ModalPrompt.js";
|
||||||
import {NavWindow} from "./NavWindow.js";
|
import {NavWindow} from "./NavWindow.js";
|
||||||
|
|
||||||
export class NavDragDrop {
|
export class NavDragDrop {
|
||||||
@ -27,65 +28,148 @@ export class NavDragDrop {
|
|||||||
* @param {NavWindow} nav_window_ref
|
* @param {NavWindow} nav_window_ref
|
||||||
*/
|
*/
|
||||||
constructor(drop_area, nav_window_ref) {
|
constructor(drop_area, nav_window_ref) {
|
||||||
drop_area.addEventListener("dragenter", this);
|
drop_area.addEventListener("dragenter", this, false);
|
||||||
drop_area.addEventListener("dragover", this);
|
drop_area.addEventListener("dragover", this, false);
|
||||||
drop_area.addEventListener("dragleave", this);
|
drop_area.addEventListener("dragleave", this, false);
|
||||||
drop_area.addEventListener("drop", this);
|
drop_area.addEventListener("drop", this, false);
|
||||||
this.drop_area = drop_area;
|
this.drop_area = drop_area;
|
||||||
this.nav_window_ref = nav_window_ref;
|
this.nav_window_ref = nav_window_ref;
|
||||||
|
this.modal_prompt = new ModalPrompt();
|
||||||
}
|
}
|
||||||
|
|
||||||
handleEvent(e) {
|
/**
|
||||||
e.preventDefault();
|
*
|
||||||
|
* @param {FileSystemEntry} item
|
||||||
|
* @param {string} path
|
||||||
|
* @returns {Promise<FileUpload[]>}
|
||||||
|
*/
|
||||||
|
async scan_files(item, path) {
|
||||||
|
let new_uploads = [];
|
||||||
|
if (item.isDirectory) {
|
||||||
|
if (!path && !await this.modal_prompt.confirm(`Copy whole directory: ${item.fullPath}?`, "", true))
|
||||||
|
return new_uploads;
|
||||||
|
let directoryReader = item.createReader();
|
||||||
|
let promise = new Promise((resolve, reject) => {
|
||||||
|
directoryReader.readEntries(async (entries) => {
|
||||||
|
for (const entry of entries) {
|
||||||
|
new_uploads.push(... await this.scan_files(entry, path + item.name + "/"));
|
||||||
|
}
|
||||||
|
resolve();
|
||||||
|
});
|
||||||
|
})
|
||||||
|
await promise;
|
||||||
|
} else {
|
||||||
|
let promise = new Promise((resolve, reject) => {
|
||||||
|
item.file((file) => {
|
||||||
|
resolve(file);
|
||||||
|
})
|
||||||
|
});
|
||||||
|
new_uploads.push(new FileUpload(await promise, this.nav_window_ref, path));
|
||||||
|
}
|
||||||
|
return new_uploads;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @param {DataTransferItemList} items
|
||||||
|
* @returns {Promise<DataTransferItemList>}
|
||||||
|
*/
|
||||||
|
handle_drop_advanced(items) {
|
||||||
|
return new Promise(async (resolve, reject) => {
|
||||||
|
let uploads = [];
|
||||||
|
for (let i = 0; i < items.length; i++) {
|
||||||
|
let item = items[i]?.webkitGetAsEntry?.() ?? items[i]?.getAsEntry?.() ?? null;
|
||||||
|
let path = "";
|
||||||
|
if (item) {
|
||||||
|
let new_uploads = await this.scan_files(item, path);
|
||||||
|
console.log(new_uploads);
|
||||||
|
uploads.push(... new_uploads);
|
||||||
|
} else {
|
||||||
|
reject();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
resolve(uploads);
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @param {FileUpload[]} uploads
|
||||||
|
* @returns {FileUpload[]}
|
||||||
|
*/
|
||||||
|
async handle_conflicts(uploads) {
|
||||||
|
let keepers = [];
|
||||||
|
let requests = {};
|
||||||
|
for (let upload of uploads) {
|
||||||
|
if (!await upload.check_if_exists()) {
|
||||||
|
keepers.push(upload.filename);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
let request = {};
|
||||||
|
request.label = upload.filename;
|
||||||
|
request.type = "checkbox";
|
||||||
|
let id = upload.filename;
|
||||||
|
requests[id] = request;
|
||||||
|
}
|
||||||
|
if (Object.keys(requests).length > 0) {
|
||||||
|
let responses = await this.nav_window_ref.modal_prompt.prompt(
|
||||||
|
"Conflicts found while uploading. Replace?",
|
||||||
|
requests
|
||||||
|
)
|
||||||
|
if (responses === null)
|
||||||
|
return null;
|
||||||
|
for (let key of Object.keys(responses)) {
|
||||||
|
if (responses[key])
|
||||||
|
keepers.push(key);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return uploads.filter((upload) => keepers.includes(upload.filename));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @param {Event} e
|
||||||
|
*/
|
||||||
|
async handleEvent(e) {
|
||||||
switch(e.type){
|
switch(e.type){
|
||||||
case "dragenter":
|
case "dragenter":
|
||||||
|
e.preventDefault();
|
||||||
|
e.stopPropagation();
|
||||||
this.drop_area.classList.add("drag-enter");
|
this.drop_area.classList.add("drag-enter");
|
||||||
break;
|
break;
|
||||||
case "dragover":
|
case "dragover":
|
||||||
|
e.preventDefault();
|
||||||
|
e.stopPropagation();
|
||||||
break;
|
break;
|
||||||
case "dragleave":
|
case "dragleave":
|
||||||
|
e.preventDefault();
|
||||||
|
e.stopPropagation();
|
||||||
this.drop_area.classList.remove("drag-enter");
|
this.drop_area.classList.remove("drag-enter");
|
||||||
break;
|
break;
|
||||||
case "drop":
|
case "drop":
|
||||||
if (e.dataTransfer.items) {
|
let uploads;
|
||||||
for (let item of e.dataTransfer.items) {
|
let items = e.dataTransfer.items;
|
||||||
if (item.kind === 'file') {
|
e.preventDefault();
|
||||||
var file = item.getAsFile();
|
e.stopPropagation();
|
||||||
if (file.type === "" && file.size !== 0) {
|
try {
|
||||||
this.nav_window_ref.modal_prompt.alert(file.name + ": Cannot upload folders.");
|
uploads = await this.handle_drop_advanced(items);
|
||||||
continue;
|
} catch {
|
||||||
}
|
uploads = [];
|
||||||
if (file.size === 0) {
|
for (let file of e.dataTransfer.files) {
|
||||||
var proc = cockpit.spawn(
|
let uploader = new FileUpload(file, this.nav_window_ref);
|
||||||
["/usr/share/cockpit/navigator/scripts/touch.py3", this.nav_window_ref.pwd().path_str() + "/" + file.name],
|
uploader.using_webkit = false;
|
||||||
{superuser: "try", err: "out"}
|
uploads.push(uploader);
|
||||||
);
|
|
||||||
proc.done(() => {
|
|
||||||
this.nav_window_ref.refresh();
|
|
||||||
});
|
|
||||||
proc.fail((e, data) => {
|
|
||||||
this.nav_window_ref.modal_prompt.alert(data);
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
var uploader = new FileUpload(file, this.nav_window_ref);
|
|
||||||
uploader.upload();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
for (let file of ev.dataTransfer.files) {
|
|
||||||
if (file.type === "")
|
|
||||||
continue;
|
|
||||||
var uploader = new FileUpload(file, this.nav_window_ref);
|
|
||||||
uploader.upload();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
this.drop_area.classList.remove("drag-enter");
|
this.drop_area.classList.remove("drag-enter");
|
||||||
|
if (uploads.length === 0)
|
||||||
|
break;
|
||||||
|
uploads = await this.handle_conflicts(uploads);
|
||||||
|
uploads.forEach((upload) => {upload.upload()});
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
this.drop_area.classList.remove("drag-enter");
|
this.drop_area.classList.remove("drag-enter");
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
e.stopPropagation();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -35,6 +35,7 @@
|
|||||||
--nav-border-radius: 4px;
|
--nav-border-radius: 4px;
|
||||||
--symlink-symbol-color: var(--navigation);
|
--symlink-symbol-color: var(--navigation);
|
||||||
--list-view-header: var(--selected);
|
--list-view-header: var(--selected);
|
||||||
|
--outline-color: black;
|
||||||
}
|
}
|
||||||
|
|
||||||
[data-theme="dark"] {
|
[data-theme="dark"] {
|
||||||
@ -53,6 +54,11 @@
|
|||||||
--nav-entry-color: #555F6E;
|
--nav-entry-color: #555F6E;
|
||||||
--symlink-symbol-color: var(--navigation);
|
--symlink-symbol-color: var(--navigation);
|
||||||
--list-view-header: var(--container);
|
--list-view-header: var(--container);
|
||||||
|
--outline-color: white;
|
||||||
|
}
|
||||||
|
|
||||||
|
button {
|
||||||
|
outline-color: var(--outline-color) !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
html {
|
html {
|
||||||
|
@ -32,27 +32,35 @@ import sys
|
|||||||
import json
|
import json
|
||||||
|
|
||||||
def write_chunk(chunk, file):
|
def write_chunk(chunk, file):
|
||||||
|
if not file:
|
||||||
|
path = sys.argv[1]
|
||||||
|
parent_path = os.path.dirname(path)
|
||||||
|
if not os.path.exists(parent_path):
|
||||||
|
os.makedirs(parent_path, exist_ok=True)
|
||||||
|
elif os.path.isfile(parent_path):
|
||||||
|
print(parent_path + ": exists and is not a directory.")
|
||||||
|
sys.exit(1)
|
||||||
|
try:
|
||||||
|
file = open(path, "wb")
|
||||||
|
except Exception as e:
|
||||||
|
print(e)
|
||||||
|
sys.exit(1)
|
||||||
seek = chunk["seek"]
|
seek = chunk["seek"]
|
||||||
data = base64.b64decode(chunk["chunk"])
|
data = base64.b64decode(chunk["chunk"])
|
||||||
file.seek(seek)
|
file.seek(seek)
|
||||||
file.write(data)
|
file.write(data)
|
||||||
|
|
||||||
def main():
|
def main():
|
||||||
|
file = None
|
||||||
if len(sys.argv) != 2:
|
if len(sys.argv) != 2:
|
||||||
print("Invalid number of arguments.")
|
print("Invalid number of arguments.")
|
||||||
sys.exit(1)
|
sys.exit(1)
|
||||||
path = sys.argv[1]
|
|
||||||
try:
|
|
||||||
file = open(path, "wb")
|
|
||||||
except Exception as e:
|
|
||||||
print(e)
|
|
||||||
sys.exit(1)
|
|
||||||
while True:
|
while True:
|
||||||
try:
|
try:
|
||||||
json_in = input()
|
json_in = input()
|
||||||
except EOFError:
|
except EOFError:
|
||||||
break
|
break
|
||||||
json_list = json_in.split("\n")
|
json_list = json_in.split("\n") # need to split in case writes happen faster than reads
|
||||||
for json_obj in json_list:
|
for json_obj in json_list:
|
||||||
try:
|
try:
|
||||||
obj_in = json.loads(json_obj)
|
obj_in = json.loads(json_obj)
|
||||||
@ -63,7 +71,8 @@ def main():
|
|||||||
log.close()
|
log.close()
|
||||||
sys.exit(1)
|
sys.exit(1)
|
||||||
write_chunk(obj_in, file)
|
write_chunk(obj_in, file)
|
||||||
file.close()
|
if file:
|
||||||
|
file.close()
|
||||||
sys.exit(0)
|
sys.exit(0)
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
|
@ -32,6 +32,9 @@ rm -rf %{buildroot}
|
|||||||
/usr/share/cockpit/navigator/*
|
/usr/share/cockpit/navigator/*
|
||||||
|
|
||||||
%changelog
|
%changelog
|
||||||
|
* Fri Jul 16 2021 Josh Boudreau <jboudreau@45drives.com> 0.5.2-1
|
||||||
|
- Implement uploading of entire directories.
|
||||||
|
- Add cancel option to in-progress file uploads.
|
||||||
* Thu Jul 15 2021 Josh Boudreau <jboudreau@45drives.com> 0.5.1-1
|
* Thu Jul 15 2021 Josh Boudreau <jboudreau@45drives.com> 0.5.1-1
|
||||||
- Allow modal popups to scroll if overflowing past page.
|
- Allow modal popups to scroll if overflowing past page.
|
||||||
- Moves focus to next input in modal popup when enter is pressed.
|
- Moves focus to next input in modal popup when enter is pressed.
|
||||||
|
@ -32,6 +32,9 @@ rm -rf %{buildroot}
|
|||||||
/usr/share/cockpit/navigator/*
|
/usr/share/cockpit/navigator/*
|
||||||
|
|
||||||
%changelog
|
%changelog
|
||||||
|
* Fri Jul 16 2021 Josh Boudreau <jboudreau@45drives.com> 0.5.2-1
|
||||||
|
- Implement uploading of entire directories.
|
||||||
|
- Add cancel option to in-progress file uploads.
|
||||||
* Thu Jul 15 2021 Josh Boudreau <jboudreau@45drives.com> 0.5.1-1
|
* Thu Jul 15 2021 Josh Boudreau <jboudreau@45drives.com> 0.5.1-1
|
||||||
- Allow modal popups to scroll if overflowing past page.
|
- Allow modal popups to scroll if overflowing past page.
|
||||||
- Moves focus to next input in modal popup when enter is pressed.
|
- Moves focus to next input in modal popup when enter is pressed.
|
||||||
|
@ -1,3 +1,10 @@
|
|||||||
|
cockpit-navigator (0.5.2-1focal) focal; urgency=medium
|
||||||
|
|
||||||
|
* Implement uploading of entire directories.
|
||||||
|
* Add cancel option to in-progress file uploads.
|
||||||
|
|
||||||
|
-- Josh Boudreau <jboudreau@45drives.com> Fri, 16 Jul 2021 13:56:55 -0300
|
||||||
|
|
||||||
cockpit-navigator (0.5.1-1focal) focal; urgency=medium
|
cockpit-navigator (0.5.1-1focal) focal; urgency=medium
|
||||||
|
|
||||||
* Allow modal popups to scroll if overflowing past page.
|
* Allow modal popups to scroll if overflowing past page.
|
||||||
|
Loading…
x
Reference in New Issue
Block a user