Allow server to make backups
This commit is contained in:
parent
aeceae11d5
commit
b3a827e042
27 changed files with 891 additions and 418 deletions
65
internal/component/static/src/control/highlight.ts
Normal file
65
internal/component/static/src/control/highlight.ts
Normal file
|
|
@ -0,0 +1,65 @@
|
|||
|
||||
const types: Record<string, (element: HTMLElement) => HTMLElement | string> = {
|
||||
"date": (element) => {
|
||||
return (new Date(element.innerText)).toISOString()
|
||||
},
|
||||
"path": (element) => {
|
||||
const text = element.innerText.split("/");
|
||||
return text[text.length - 1];
|
||||
},
|
||||
"pathbuilders": () => {
|
||||
const pathbuilders: {[name: string]: string} = (window as any).pathbuilders; // must be declared globally on page!
|
||||
const wrapper = document.createElement("span");
|
||||
|
||||
let found_one = false
|
||||
Object.keys(pathbuilders).forEach(name => {
|
||||
found_one = true
|
||||
|
||||
const filename = name + ".xml"
|
||||
const data = pathbuilders[name]
|
||||
const mime = "application/xml"
|
||||
wrapper.append(make_download_link(filename, name, data, mime))
|
||||
wrapper.append(document.createTextNode(" "))
|
||||
})
|
||||
|
||||
if (!found_one) return '(none)';
|
||||
|
||||
const small = document.createElement('small')
|
||||
small.append(document.createTextNode("(click to download)"))
|
||||
wrapper.append(small)
|
||||
|
||||
return wrapper
|
||||
}
|
||||
}
|
||||
|
||||
const make_download_link = (filename: string, title: string, content: string, type: string) => {
|
||||
const blob = new Blob(
|
||||
[content],
|
||||
{
|
||||
type: type ?? "text/plain"
|
||||
}
|
||||
);
|
||||
|
||||
const link = document.createElement("a")
|
||||
link.target = "_blank"
|
||||
link.download = filename
|
||||
link.href = URL.createObjectURL(blob)
|
||||
link.append(document.createTextNode(title))
|
||||
|
||||
return link
|
||||
}
|
||||
|
||||
Object.keys(types).forEach(key => {
|
||||
const f = types[key];
|
||||
const elements = document.querySelectorAll("code." + key) as NodeListOf<HTMLElement>
|
||||
elements.forEach(element => {
|
||||
const newElement = f(element)
|
||||
if (typeof newElement === 'string') {
|
||||
element.innerHTML = ""
|
||||
element.appendChild(document.createTextNode(newElement))
|
||||
return
|
||||
}
|
||||
|
||||
element.parentNode!.replaceChild(newElement, element)
|
||||
})
|
||||
})
|
||||
|
|
@ -11,3 +11,7 @@
|
|||
.wisski.stopped {
|
||||
background-color: #ff7a7a;
|
||||
}
|
||||
|
||||
.remote-action-out {
|
||||
font-size: small;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,68 +1,5 @@
|
|||
import '../global.ts';
|
||||
import './index.css';
|
||||
|
||||
|
||||
const types: Record<string, (element: HTMLElement) => HTMLElement | string> = {
|
||||
"date": (element) => {
|
||||
return (new Date(element.innerText)).toISOString()
|
||||
},
|
||||
"path": (element) => {
|
||||
const text = element.innerText.split("/");
|
||||
return text[text.length - 1];
|
||||
},
|
||||
"pathbuilders": () => {
|
||||
const pathbuilders: {[name: string]: string} = (window as any).pathbuilders; // must be declared globally on page!
|
||||
const wrapper = document.createElement("span");
|
||||
|
||||
let found_one = false
|
||||
Object.keys(pathbuilders).forEach(name => {
|
||||
found_one = true
|
||||
|
||||
const filename = name + ".xml"
|
||||
const data = pathbuilders[name]
|
||||
const mime = "application/xml"
|
||||
wrapper.append(make_download_link(filename, name, data, mime))
|
||||
wrapper.append(document.createTextNode(" "))
|
||||
})
|
||||
|
||||
if (!found_one) return '(none)';
|
||||
|
||||
const small = document.createElement('small')
|
||||
small.append(document.createTextNode("(click to download)"))
|
||||
wrapper.append(small)
|
||||
|
||||
return wrapper
|
||||
}
|
||||
}
|
||||
|
||||
const make_download_link = (filename: string, title: string, content: string, type: string) => {
|
||||
const blob = new Blob(
|
||||
[content],
|
||||
{
|
||||
type: type ?? "text/plain"
|
||||
}
|
||||
);
|
||||
|
||||
const link = document.createElement("a")
|
||||
link.target = "_blank"
|
||||
link.download = filename
|
||||
link.href = URL.createObjectURL(blob)
|
||||
link.append(document.createTextNode(title))
|
||||
|
||||
return link
|
||||
}
|
||||
|
||||
Object.keys(types).forEach(key => {
|
||||
const f = types[key];
|
||||
const elements = document.querySelectorAll("code." + key) as NodeListOf<HTMLElement>
|
||||
elements.forEach(element => {
|
||||
const newElement = f(element)
|
||||
if (typeof newElement === 'string') {
|
||||
element.innerHTML = ""
|
||||
element.appendChild(document.createTextNode(newElement))
|
||||
return
|
||||
}
|
||||
|
||||
element.parentNode!.replaceChild(newElement, element)
|
||||
})
|
||||
})
|
||||
import './highlight.ts';
|
||||
import './remote.ts';
|
||||
|
|
|
|||
62
internal/component/static/src/control/remote.ts
Normal file
62
internal/component/static/src/control/remote.ts
Normal file
|
|
@ -0,0 +1,62 @@
|
|||
import connectSocket from '../socket/socket';
|
||||
|
||||
const elements = document.getElementsByClassName('remote-action')
|
||||
Array.from(elements).forEach((element) => {
|
||||
const action = element.getAttribute('data-action') as string;
|
||||
const param = element.getAttribute('data-param') as string | undefined;
|
||||
const target = document.querySelector(element.getAttribute('data-target')!) as HTMLElement;
|
||||
const bufferSize = (function() {
|
||||
const number = parseInt(element.getAttribute('data-buffer') ?? "", 10) ?? 0;
|
||||
return (isFinite(number) && number > 0) ? number : 0;
|
||||
})()
|
||||
|
||||
let running = false
|
||||
element.addEventListener('click', function(ev) {
|
||||
ev.preventDefault();
|
||||
|
||||
// already running
|
||||
if (running) return
|
||||
|
||||
running = true
|
||||
element.setAttribute('disabled', 'disabled');
|
||||
const close = function() {
|
||||
element.removeAttribute('disabled');
|
||||
running = false;
|
||||
}
|
||||
|
||||
target.innerText = "";
|
||||
|
||||
const buffer: Array<string> = [];
|
||||
const println = function(line: string) {
|
||||
if(bufferSize === 0) {
|
||||
target.innerText += line + "\n";
|
||||
return;
|
||||
}
|
||||
|
||||
buffer.push(line);
|
||||
if(buffer.length > bufferSize) {
|
||||
buffer.splice(0, buffer.length - bufferSize)
|
||||
}
|
||||
target.innerText = buffer.join("\n");
|
||||
}
|
||||
|
||||
println("Connecting ...")
|
||||
|
||||
// connect to the socket and send the action
|
||||
connectSocket((socket) => {
|
||||
println("Connected")
|
||||
socket.send(action);
|
||||
if (typeof param === 'string') {
|
||||
socket.send(param);
|
||||
}
|
||||
}, (data) => {
|
||||
println(data);
|
||||
}).then(() => {
|
||||
println("Connection closed.\n")
|
||||
close();
|
||||
}).catch(() => {
|
||||
println("Connection errored.\n")
|
||||
close();
|
||||
});
|
||||
});
|
||||
})
|
||||
11
internal/component/static/src/socket/socket.ts
Normal file
11
internal/component/static/src/socket/socket.ts
Normal file
|
|
@ -0,0 +1,11 @@
|
|||
export default function connectSocket(onOpen: (socket: WebSocket) => void, onData: (data: any) => void): Promise<CloseEvent> {
|
||||
return new Promise((rs, rj) => {
|
||||
const socket = new WebSocket(location.href.replace('http', 'ws'));
|
||||
|
||||
socket.onclose = rs;
|
||||
socket.onerror = rj;
|
||||
|
||||
socket.onmessage = (ev) => onData(ev.data)
|
||||
socket.onopen = () => onOpen(socket);
|
||||
});
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue