mirror of
https://github.com/XZB-1248/Spark
synced 2025-10-03 07:22:07 +08:00
optimize: reindent front-end with tab
This commit is contained in:
@@ -17,195 +17,195 @@ let bytes = 0;
|
||||
let ticks = 0;
|
||||
let title = i18n.t('DESKTOP.TITLE');
|
||||
function ScreenModal(props) {
|
||||
const [bandwidth, setBandwidth] = useState(0);
|
||||
const [fps, setFps] = useState(0);
|
||||
const canvasRef = useCallback((e) => {
|
||||
if (e && props.visible && !conn) {
|
||||
canvas = e;
|
||||
initCanvas(canvas);
|
||||
construct(canvas);
|
||||
}
|
||||
}, [props]);
|
||||
useEffect(() => {
|
||||
if (props.visible) {
|
||||
secret = CryptoJS.enc.Hex.parse(genRandHex(32));
|
||||
} else {
|
||||
if (ws && conn) {
|
||||
clearInterval(ticker);
|
||||
ws.close();
|
||||
conn = false;
|
||||
}
|
||||
}
|
||||
}, [props.visible, props.device]);
|
||||
const [bandwidth, setBandwidth] = useState(0);
|
||||
const [fps, setFps] = useState(0);
|
||||
const canvasRef = useCallback((e) => {
|
||||
if (e && props.visible && !conn) {
|
||||
canvas = e;
|
||||
initCanvas(canvas);
|
||||
construct(canvas);
|
||||
}
|
||||
}, [props]);
|
||||
useEffect(() => {
|
||||
if (props.visible) {
|
||||
secret = CryptoJS.enc.Hex.parse(genRandHex(32));
|
||||
} else {
|
||||
if (ws && conn) {
|
||||
clearInterval(ticker);
|
||||
ws.close();
|
||||
conn = false;
|
||||
}
|
||||
}
|
||||
}, [props.visible, props.device]);
|
||||
|
||||
function initCanvas() {
|
||||
if (!canvas) return;
|
||||
ctx = canvas.getContext('2d', {alpha: false});
|
||||
}
|
||||
function construct() {
|
||||
if (ctx !== null) {
|
||||
if (ws !== null && conn) {
|
||||
ws.close();
|
||||
}
|
||||
ws = new WebSocket(getBaseURL(true, `api/device/desktop?device=${props.device.id}&secret=${secret}`));
|
||||
ws.binaryType = 'arraybuffer';
|
||||
ws.onopen = () => {
|
||||
conn = true;
|
||||
}
|
||||
ws.onmessage = (e) => {
|
||||
parseBlocks(e.data, canvas, ctx);
|
||||
};
|
||||
ws.onclose = () => {
|
||||
if (conn) {
|
||||
conn = false;
|
||||
message.warn(i18n.t('COMMON.DISCONNECTED'));
|
||||
}
|
||||
};
|
||||
ws.onerror = (e) => {
|
||||
console.error(e);
|
||||
if (conn) {
|
||||
conn = false;
|
||||
message.warn(i18n.t('COMMON.DISCONNECTED'));
|
||||
} else {
|
||||
message.warn(i18n.t('COMMON.CONNECTION_FAILED'));
|
||||
}
|
||||
};
|
||||
clearInterval(ticker);
|
||||
ticker = setInterval(() => {
|
||||
setBandwidth(bytes);
|
||||
setFps(frames);
|
||||
bytes = 0;
|
||||
frames = 0;
|
||||
ticks++;
|
||||
if (ticks > 10 && conn) {
|
||||
ticks = 0;
|
||||
ws.send(encrypt({
|
||||
act: 'DESKTOP_PING'
|
||||
}, secret));
|
||||
}
|
||||
}, 1000);
|
||||
}
|
||||
}
|
||||
function fullScreen() {
|
||||
try {
|
||||
canvas.requestFullscreen();
|
||||
} catch {}
|
||||
try {
|
||||
canvas.webkitRequestFullscreen();
|
||||
} catch {}
|
||||
try {
|
||||
canvas.mozRequestFullScreen();
|
||||
} catch {}
|
||||
try {
|
||||
canvas.msRequestFullscreen();
|
||||
} catch {}
|
||||
}
|
||||
function refresh() {
|
||||
if (canvas && props.visible) {
|
||||
if (!conn) {
|
||||
canvas.width = 1920;
|
||||
canvas.height = 1080;
|
||||
initCanvas(canvas);
|
||||
construct(canvas);
|
||||
} else {
|
||||
ws.send(encrypt({
|
||||
act: 'DESKTOP_SHOT'
|
||||
}, secret));
|
||||
}
|
||||
}
|
||||
}
|
||||
function initCanvas() {
|
||||
if (!canvas) return;
|
||||
ctx = canvas.getContext('2d', {alpha: false});
|
||||
}
|
||||
function construct() {
|
||||
if (ctx !== null) {
|
||||
if (ws !== null && conn) {
|
||||
ws.close();
|
||||
}
|
||||
ws = new WebSocket(getBaseURL(true, `api/device/desktop?device=${props.device.id}&secret=${secret}`));
|
||||
ws.binaryType = 'arraybuffer';
|
||||
ws.onopen = () => {
|
||||
conn = true;
|
||||
}
|
||||
ws.onmessage = (e) => {
|
||||
parseBlocks(e.data, canvas, ctx);
|
||||
};
|
||||
ws.onclose = () => {
|
||||
if (conn) {
|
||||
conn = false;
|
||||
message.warn(i18n.t('COMMON.DISCONNECTED'));
|
||||
}
|
||||
};
|
||||
ws.onerror = (e) => {
|
||||
console.error(e);
|
||||
if (conn) {
|
||||
conn = false;
|
||||
message.warn(i18n.t('COMMON.DISCONNECTED'));
|
||||
} else {
|
||||
message.warn(i18n.t('COMMON.CONNECTION_FAILED'));
|
||||
}
|
||||
};
|
||||
clearInterval(ticker);
|
||||
ticker = setInterval(() => {
|
||||
setBandwidth(bytes);
|
||||
setFps(frames);
|
||||
bytes = 0;
|
||||
frames = 0;
|
||||
ticks++;
|
||||
if (ticks > 10 && conn) {
|
||||
ticks = 0;
|
||||
ws.send(encrypt({
|
||||
act: 'DESKTOP_PING'
|
||||
}, secret));
|
||||
}
|
||||
}, 1000);
|
||||
}
|
||||
}
|
||||
function fullScreen() {
|
||||
try {
|
||||
canvas.requestFullscreen();
|
||||
} catch {}
|
||||
try {
|
||||
canvas.webkitRequestFullscreen();
|
||||
} catch {}
|
||||
try {
|
||||
canvas.mozRequestFullScreen();
|
||||
} catch {}
|
||||
try {
|
||||
canvas.msRequestFullscreen();
|
||||
} catch {}
|
||||
}
|
||||
function refresh() {
|
||||
if (canvas && props.visible) {
|
||||
if (!conn) {
|
||||
canvas.width = 1920;
|
||||
canvas.height = 1080;
|
||||
initCanvas(canvas);
|
||||
construct(canvas);
|
||||
} else {
|
||||
ws.send(encrypt({
|
||||
act: 'DESKTOP_SHOT'
|
||||
}, secret));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function parseBlocks(ab, canvas, canvasCtx) {
|
||||
ab = ab.slice(5);
|
||||
let dv = new DataView(ab);
|
||||
let op = dv.getUint8(0);
|
||||
if (op === 3) {
|
||||
handleJSON(ab.slice(1));
|
||||
return;
|
||||
}
|
||||
if (op === 2) {
|
||||
let width = dv.getUint16(1, false);
|
||||
let height = dv.getUint16(3, false);
|
||||
if (width === 0 || height === 0) return;
|
||||
canvas.width = width;
|
||||
canvas.height = height;
|
||||
return;
|
||||
}
|
||||
if (op === 0) frames++;
|
||||
bytes += ab.byteLength;
|
||||
let offset = 1;
|
||||
while (offset < ab.byteLength) {
|
||||
let it = dv.getUint16(offset + 0, false); // image type
|
||||
let il = dv.getUint16(offset + 2, false); // image length
|
||||
let dx = dv.getUint16(offset + 4, false); // image block x
|
||||
let dy = dv.getUint16(offset + 6, false); // image block y
|
||||
let bw = dv.getUint16(offset + 8, false); // image block width
|
||||
let bh = dv.getUint16(offset + 10, false); // image block height
|
||||
offset += 12;
|
||||
updateImage(ab.slice(offset, offset + il), it, dx, dy, bw, bh, canvasCtx);
|
||||
offset += il;
|
||||
}
|
||||
dv = null;
|
||||
}
|
||||
function updateImage(ab, it, dx, dy, bw, bh, canvasCtx) {
|
||||
if (it === 0) {
|
||||
canvasCtx.putImageData(new ImageData(new Uint8ClampedArray(ab), bw, bh), dx, dy, 0, 0, bw, bh);
|
||||
} else {
|
||||
createImageBitmap(new Blob([ab]), 0, 0, bw, bh)
|
||||
.then((ib) => {
|
||||
canvasCtx.drawImage(ib, 0, 0, bw, bh, dx, dy, bw, bh);
|
||||
});
|
||||
}
|
||||
}
|
||||
function handleJSON(ab) {
|
||||
let data = decrypt(ab, secret);
|
||||
try {
|
||||
data = JSON.parse(data);
|
||||
} catch (_) {}
|
||||
if (data?.act === 'WARN') {
|
||||
message.warn(data.msg ? translate(data.msg) : i18n.t('COMMON.UNKNOWN_ERROR'));
|
||||
return;
|
||||
}
|
||||
if (data?.act === 'QUIT') {
|
||||
message.warn(data.msg ? translate(data.msg) : i18n.t('COMMON.UNKNOWN_ERROR'));
|
||||
conn = false;
|
||||
ws.close();
|
||||
}
|
||||
}
|
||||
function parseBlocks(ab, canvas, canvasCtx) {
|
||||
ab = ab.slice(5);
|
||||
let dv = new DataView(ab);
|
||||
let op = dv.getUint8(0);
|
||||
if (op === 3) {
|
||||
handleJSON(ab.slice(1));
|
||||
return;
|
||||
}
|
||||
if (op === 2) {
|
||||
let width = dv.getUint16(1, false);
|
||||
let height = dv.getUint16(3, false);
|
||||
if (width === 0 || height === 0) return;
|
||||
canvas.width = width;
|
||||
canvas.height = height;
|
||||
return;
|
||||
}
|
||||
if (op === 0) frames++;
|
||||
bytes += ab.byteLength;
|
||||
let offset = 1;
|
||||
while (offset < ab.byteLength) {
|
||||
let it = dv.getUint16(offset + 0, false); // image type
|
||||
let il = dv.getUint16(offset + 2, false); // image length
|
||||
let dx = dv.getUint16(offset + 4, false); // image block x
|
||||
let dy = dv.getUint16(offset + 6, false); // image block y
|
||||
let bw = dv.getUint16(offset + 8, false); // image block width
|
||||
let bh = dv.getUint16(offset + 10, false); // image block height
|
||||
offset += 12;
|
||||
updateImage(ab.slice(offset, offset + il), it, dx, dy, bw, bh, canvasCtx);
|
||||
offset += il;
|
||||
}
|
||||
dv = null;
|
||||
}
|
||||
function updateImage(ab, it, dx, dy, bw, bh, canvasCtx) {
|
||||
if (it === 0) {
|
||||
canvasCtx.putImageData(new ImageData(new Uint8ClampedArray(ab), bw, bh), dx, dy, 0, 0, bw, bh);
|
||||
} else {
|
||||
createImageBitmap(new Blob([ab]), 0, 0, bw, bh)
|
||||
.then((ib) => {
|
||||
canvasCtx.drawImage(ib, 0, 0, bw, bh, dx, dy, bw, bh);
|
||||
});
|
||||
}
|
||||
}
|
||||
function handleJSON(ab) {
|
||||
let data = decrypt(ab, secret);
|
||||
try {
|
||||
data = JSON.parse(data);
|
||||
} catch (_) {}
|
||||
if (data?.act === 'WARN') {
|
||||
message.warn(data.msg ? translate(data.msg) : i18n.t('COMMON.UNKNOWN_ERROR'));
|
||||
return;
|
||||
}
|
||||
if (data?.act === 'QUIT') {
|
||||
message.warn(data.msg ? translate(data.msg) : i18n.t('COMMON.UNKNOWN_ERROR'));
|
||||
conn = false;
|
||||
ws.close();
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
<DraggableModal
|
||||
draggable={true}
|
||||
maskClosable={false}
|
||||
destroyOnClose={true}
|
||||
modalTitle={`${title} ${formatSize(bandwidth)}/s FPS: ${fps}`}
|
||||
footer={null}
|
||||
height={480}
|
||||
width={940}
|
||||
bodyStyle={{
|
||||
padding: 0
|
||||
}}
|
||||
{...props}
|
||||
>
|
||||
<canvas
|
||||
id='painter'
|
||||
ref={canvasRef}
|
||||
style={{width: '100%', height: '100%'}}
|
||||
/>
|
||||
<Button
|
||||
style={{right:'59px'}}
|
||||
className='header-button'
|
||||
icon={<FullscreenOutlined />}
|
||||
onClick={fullScreen}
|
||||
/>
|
||||
<Button
|
||||
style={{right:'115px'}}
|
||||
className='header-button'
|
||||
icon={<ReloadOutlined />}
|
||||
onClick={refresh}
|
||||
/>
|
||||
</DraggableModal>
|
||||
);
|
||||
return (
|
||||
<DraggableModal
|
||||
draggable={true}
|
||||
maskClosable={false}
|
||||
destroyOnClose={true}
|
||||
modalTitle={`${title} ${formatSize(bandwidth)}/s FPS: ${fps}`}
|
||||
footer={null}
|
||||
height={480}
|
||||
width={940}
|
||||
bodyStyle={{
|
||||
padding: 0
|
||||
}}
|
||||
{...props}
|
||||
>
|
||||
<canvas
|
||||
id='painter'
|
||||
ref={canvasRef}
|
||||
style={{width: '100%', height: '100%'}}
|
||||
/>
|
||||
<Button
|
||||
style={{right:'59px'}}
|
||||
className='header-button'
|
||||
icon={<FullscreenOutlined />}
|
||||
onClick={fullScreen}
|
||||
/>
|
||||
<Button
|
||||
style={{right:'115px'}}
|
||||
className='header-button'
|
||||
icon={<ReloadOutlined />}
|
||||
onClick={refresh}
|
||||
/>
|
||||
</DraggableModal>
|
||||
);
|
||||
}
|
||||
|
||||
export default ScreenModal;
|
@@ -1,45 +1,45 @@
|
||||
a {
|
||||
user-select: none;
|
||||
user-select: none;
|
||||
}
|
||||
|
||||
.file-row {
|
||||
user-select: none;
|
||||
cursor: pointer;
|
||||
user-select: none;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.ant-table-body {
|
||||
max-height: 300px;
|
||||
min-height: 300px;
|
||||
max-height: 300px;
|
||||
min-height: 300px;
|
||||
}
|
||||
|
||||
.ant-breadcrumb {
|
||||
overflow-x: hidden;
|
||||
white-space: nowrap;
|
||||
overflow-x: hidden;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.ant-pro-table-list-toolbar {
|
||||
overflow: hidden;
|
||||
-ms-overflow-style: none;
|
||||
scrollbar-width: none;
|
||||
overflow: hidden;
|
||||
-ms-overflow-style: none;
|
||||
scrollbar-width: none;
|
||||
}
|
||||
|
||||
.ant-pro-table-list-toolbar::-webkit-scrollbar {
|
||||
display: none;
|
||||
display: none;
|
||||
}
|
||||
|
||||
.upload-progress-square > .ant-progress-outer > .ant-progress-inner {
|
||||
border-radius: 0 !important;
|
||||
border-radius: 0 !important;
|
||||
}
|
||||
|
||||
.editor-modal, .editor-modal > .ant-modal-content {
|
||||
top: 0;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
height: 100%;
|
||||
width: 100% !important;
|
||||
max-width: 100% !important;
|
||||
top: 0;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
height: 100%;
|
||||
width: 100% !important;
|
||||
max-width: 100% !important;
|
||||
}
|
||||
|
||||
.editor-modal > .ant-modal-content > .ant-modal-body {
|
||||
height: calc(100% - 110px);
|
||||
height: calc(100% - 110px);
|
||||
}
|
||||
|
File diff suppressed because it is too large
Load Diff
@@ -5,90 +5,90 @@ import prebuilt from '../config/prebuilt.json';
|
||||
import i18n from "../locale/locale";
|
||||
|
||||
function Generate(props) {
|
||||
const initValues = getInitValues();
|
||||
const initValues = getInitValues();
|
||||
|
||||
async function onFinish(form) {
|
||||
if (form?.ArchOS?.length === 2) {
|
||||
form.os = form.ArchOS[0];
|
||||
form.arch = form.ArchOS[1];
|
||||
delete form.ArchOS;
|
||||
}
|
||||
form.secure = location.protocol === 'https:' ? 'true' : 'false';
|
||||
let basePath = location.origin + location.pathname + 'api/client/';
|
||||
request(basePath + 'check', form).then(res => {
|
||||
if (res.data.code === 0) {
|
||||
post(basePath += 'generate', form);
|
||||
}
|
||||
}).catch();
|
||||
}
|
||||
async function onFinish(form) {
|
||||
if (form?.ArchOS?.length === 2) {
|
||||
form.os = form.ArchOS[0];
|
||||
form.arch = form.ArchOS[1];
|
||||
delete form.ArchOS;
|
||||
}
|
||||
form.secure = location.protocol === 'https:' ? 'true' : 'false';
|
||||
let basePath = location.origin + location.pathname + 'api/client/';
|
||||
request(basePath + 'check', form).then(res => {
|
||||
if (res.data.code === 0) {
|
||||
post(basePath += 'generate', form);
|
||||
}
|
||||
}).catch();
|
||||
}
|
||||
|
||||
function getInitValues() {
|
||||
let initValues = {
|
||||
host: location.hostname,
|
||||
port: location.port,
|
||||
path: location.pathname,
|
||||
ArchOS: ['windows', 'amd64']
|
||||
};
|
||||
if (String(location.port).length === 0) {
|
||||
initValues.port = location.protocol === 'https:' ? 443 : 80;
|
||||
}
|
||||
return initValues;
|
||||
}
|
||||
function getInitValues() {
|
||||
let initValues = {
|
||||
host: location.hostname,
|
||||
port: location.port,
|
||||
path: location.pathname,
|
||||
ArchOS: ['windows', 'amd64']
|
||||
};
|
||||
if (String(location.port).length === 0) {
|
||||
initValues.port = location.protocol === 'https:' ? 443 : 80;
|
||||
}
|
||||
return initValues;
|
||||
}
|
||||
|
||||
return (
|
||||
<ModalForm
|
||||
modalProps={{
|
||||
destroyOnClose: true,
|
||||
maskClosable: false,
|
||||
}}
|
||||
initialValues={initValues}
|
||||
onFinish={onFinish}
|
||||
submitter={{
|
||||
render: (_, elems) => elems.pop()
|
||||
}}
|
||||
{...props}
|
||||
>
|
||||
<ProFormGroup>
|
||||
<ProFormText
|
||||
width="md"
|
||||
name="host"
|
||||
label={i18n.t('GENERATOR.HOST')}
|
||||
rules={[{
|
||||
required: true
|
||||
}]}
|
||||
/>
|
||||
<ProFormDigit
|
||||
width="md"
|
||||
name="port"
|
||||
label={i18n.t('GENERATOR.PORT')}
|
||||
min={1}
|
||||
max={65535}
|
||||
rules={[{
|
||||
required: true
|
||||
}]}
|
||||
/>
|
||||
</ProFormGroup>
|
||||
<ProFormGroup>
|
||||
<ProFormText
|
||||
width="md"
|
||||
name="path"
|
||||
label={i18n.t('GENERATOR.PATH')}
|
||||
rules={[{
|
||||
required: true
|
||||
}]}
|
||||
/>
|
||||
<ProFormCascader
|
||||
width="md"
|
||||
name="ArchOS"
|
||||
label={i18n.t('GENERATOR.OS_ARCH')}
|
||||
request={() => prebuilt}
|
||||
rules={[{
|
||||
required: true
|
||||
}]}
|
||||
/>
|
||||
</ProFormGroup>
|
||||
</ModalForm>
|
||||
)
|
||||
return (
|
||||
<ModalForm
|
||||
modalProps={{
|
||||
destroyOnClose: true,
|
||||
maskClosable: false,
|
||||
}}
|
||||
initialValues={initValues}
|
||||
onFinish={onFinish}
|
||||
submitter={{
|
||||
render: (_, elems) => elems.pop()
|
||||
}}
|
||||
{...props}
|
||||
>
|
||||
<ProFormGroup>
|
||||
<ProFormText
|
||||
width="md"
|
||||
name="host"
|
||||
label={i18n.t('GENERATOR.HOST')}
|
||||
rules={[{
|
||||
required: true
|
||||
}]}
|
||||
/>
|
||||
<ProFormDigit
|
||||
width="md"
|
||||
name="port"
|
||||
label={i18n.t('GENERATOR.PORT')}
|
||||
min={1}
|
||||
max={65535}
|
||||
rules={[{
|
||||
required: true
|
||||
}]}
|
||||
/>
|
||||
</ProFormGroup>
|
||||
<ProFormGroup>
|
||||
<ProFormText
|
||||
width="md"
|
||||
name="path"
|
||||
label={i18n.t('GENERATOR.PATH')}
|
||||
rules={[{
|
||||
required: true
|
||||
}]}
|
||||
/>
|
||||
<ProFormCascader
|
||||
width="md"
|
||||
name="ArchOS"
|
||||
label={i18n.t('GENERATOR.OS_ARCH')}
|
||||
request={() => prebuilt}
|
||||
rules={[{
|
||||
required: true
|
||||
}]}
|
||||
/>
|
||||
</ProFormGroup>
|
||||
</ModalForm>
|
||||
)
|
||||
}
|
||||
|
||||
export default Generate;
|
@@ -3,66 +3,66 @@ import React, { useRef, useState } from "react";
|
||||
import Draggable from "react-draggable";
|
||||
|
||||
function DraggableModal(props) {
|
||||
const [disabled, setDisabled] = useState(true);
|
||||
const [bounds, setBounds] = useState({
|
||||
left: 0,
|
||||
top: 0,
|
||||
bottom: 0,
|
||||
right: 0,
|
||||
});
|
||||
const draggleRef = useRef(null);
|
||||
const [disabled, setDisabled] = useState(true);
|
||||
const [bounds, setBounds] = useState({
|
||||
left: 0,
|
||||
top: 0,
|
||||
bottom: 0,
|
||||
right: 0,
|
||||
});
|
||||
const draggleRef = useRef(null);
|
||||
|
||||
const onStart = (_event, uiData) => {
|
||||
const { clientWidth, clientHeight } = window.document.documentElement;
|
||||
const targetRect = draggleRef.current?.getBoundingClientRect();
|
||||
if (!targetRect || disabled) {
|
||||
return;
|
||||
}
|
||||
const onStart = (_event, uiData) => {
|
||||
const { clientWidth, clientHeight } = window.document.documentElement;
|
||||
const targetRect = draggleRef.current?.getBoundingClientRect();
|
||||
if (!targetRect || disabled) {
|
||||
return;
|
||||
}
|
||||
|
||||
setBounds({
|
||||
left: -targetRect.left + uiData.x,
|
||||
right: clientWidth - (targetRect.right - uiData.x),
|
||||
top: -targetRect.top + uiData.y,
|
||||
bottom: clientHeight - (targetRect.bottom - uiData.y),
|
||||
});
|
||||
};
|
||||
setBounds({
|
||||
left: -targetRect.left + uiData.x,
|
||||
right: clientWidth - (targetRect.right - uiData.x),
|
||||
top: -targetRect.top + uiData.y,
|
||||
bottom: clientHeight - (targetRect.bottom - uiData.y),
|
||||
});
|
||||
};
|
||||
|
||||
return (
|
||||
<Modal
|
||||
title={
|
||||
<div
|
||||
style={{
|
||||
width: '100%',
|
||||
cursor: 'move',
|
||||
}}
|
||||
onMouseOver={() => {
|
||||
if (disabled && props.draggable) setDisabled(false);
|
||||
}}
|
||||
onMouseOut={() => {
|
||||
setDisabled(true);
|
||||
}}
|
||||
onFocus={() => {}}
|
||||
onBlur={() => {
|
||||
setDisabled(true);
|
||||
}}
|
||||
>
|
||||
{props.modalTitle}
|
||||
</div>
|
||||
}
|
||||
modalRender={(modal) => (
|
||||
<Draggable
|
||||
disabled={disabled || !props.draggable}
|
||||
bounds={bounds}
|
||||
onStart={(event, uiData) => onStart(event, uiData)}
|
||||
>
|
||||
<div ref={draggleRef}>{modal}</div>
|
||||
</Draggable>
|
||||
)}
|
||||
{...props}
|
||||
>
|
||||
{props.children}
|
||||
</Modal>
|
||||
);
|
||||
return (
|
||||
<Modal
|
||||
title={
|
||||
<div
|
||||
style={{
|
||||
width: '100%',
|
||||
cursor: 'move',
|
||||
}}
|
||||
onMouseOver={() => {
|
||||
if (disabled && props.draggable) setDisabled(false);
|
||||
}}
|
||||
onMouseOut={() => {
|
||||
setDisabled(true);
|
||||
}}
|
||||
onFocus={() => {}}
|
||||
onBlur={() => {
|
||||
setDisabled(true);
|
||||
}}
|
||||
>
|
||||
{props.modalTitle}
|
||||
</div>
|
||||
}
|
||||
modalRender={(modal) => (
|
||||
<Draggable
|
||||
disabled={disabled || !props.draggable}
|
||||
bounds={bounds}
|
||||
onStart={(event, uiData) => onStart(event, uiData)}
|
||||
>
|
||||
<div ref={draggleRef}>{modal}</div>
|
||||
</Draggable>
|
||||
)}
|
||||
{...props}
|
||||
>
|
||||
{props.children}
|
||||
</Modal>
|
||||
);
|
||||
};
|
||||
|
||||
export default DraggableModal;
|
@@ -8,131 +8,131 @@ import DraggableModal from "./modal";
|
||||
import {ReloadOutlined} from "@ant-design/icons";
|
||||
|
||||
function ProcessMgr(props) {
|
||||
const [loading, setLoading] = useState(false);
|
||||
const columns = [
|
||||
{
|
||||
key: 'Name',
|
||||
title: i18n.t('PROCMGR.PROCESS'),
|
||||
dataIndex: 'name',
|
||||
ellipsis: true,
|
||||
width: 120
|
||||
},
|
||||
{
|
||||
key: 'Pid',
|
||||
title: 'Pid',
|
||||
dataIndex: 'pid',
|
||||
ellipsis: true,
|
||||
width: 40
|
||||
},
|
||||
{
|
||||
key: 'Option',
|
||||
width: 40,
|
||||
title: '',
|
||||
dataIndex: 'name',
|
||||
valueType: 'option',
|
||||
ellipsis: true,
|
||||
render: (_, file) => renderOperation(file)
|
||||
},
|
||||
];
|
||||
const options = {
|
||||
show: true,
|
||||
reload: false,
|
||||
density: false,
|
||||
setting: false,
|
||||
};
|
||||
const tableRef = useRef();
|
||||
const virtualTable = useMemo(() => {
|
||||
return VList({
|
||||
height: 300
|
||||
})
|
||||
}, []);
|
||||
useEffect(() => {
|
||||
if (props.visible) {
|
||||
setLoading(false);
|
||||
}
|
||||
}, [props.device, props.visible]);
|
||||
const [loading, setLoading] = useState(false);
|
||||
const columns = [
|
||||
{
|
||||
key: 'Name',
|
||||
title: i18n.t('PROCMGR.PROCESS'),
|
||||
dataIndex: 'name',
|
||||
ellipsis: true,
|
||||
width: 120
|
||||
},
|
||||
{
|
||||
key: 'Pid',
|
||||
title: 'Pid',
|
||||
dataIndex: 'pid',
|
||||
ellipsis: true,
|
||||
width: 40
|
||||
},
|
||||
{
|
||||
key: 'Option',
|
||||
width: 40,
|
||||
title: '',
|
||||
dataIndex: 'name',
|
||||
valueType: 'option',
|
||||
ellipsis: true,
|
||||
render: (_, file) => renderOperation(file)
|
||||
},
|
||||
];
|
||||
const options = {
|
||||
show: true,
|
||||
reload: false,
|
||||
density: false,
|
||||
setting: false,
|
||||
};
|
||||
const tableRef = useRef();
|
||||
const virtualTable = useMemo(() => {
|
||||
return VList({
|
||||
height: 300
|
||||
})
|
||||
}, []);
|
||||
useEffect(() => {
|
||||
if (props.visible) {
|
||||
setLoading(false);
|
||||
}
|
||||
}, [props.device, props.visible]);
|
||||
|
||||
function renderOperation(proc) {
|
||||
return [
|
||||
<Popconfirm
|
||||
key='kill'
|
||||
title={i18n.t('PROCMGR.KILL_PROCESS_CONFIRM')}
|
||||
onConfirm={killProcess.bind(null, proc.pid)}
|
||||
>
|
||||
<a>{i18n.t('PROCMGR.KILL_PROCESS')}</a>
|
||||
</Popconfirm>
|
||||
];
|
||||
}
|
||||
function renderOperation(proc) {
|
||||
return [
|
||||
<Popconfirm
|
||||
key='kill'
|
||||
title={i18n.t('PROCMGR.KILL_PROCESS_CONFIRM')}
|
||||
onConfirm={killProcess.bind(null, proc.pid)}
|
||||
>
|
||||
<a>{i18n.t('PROCMGR.KILL_PROCESS')}</a>
|
||||
</Popconfirm>
|
||||
];
|
||||
}
|
||||
|
||||
function killProcess(pid) {
|
||||
request(`/api/device/process/kill`, {pid: pid, device: props.device}).then(res => {
|
||||
let data = res.data;
|
||||
if (data.code === 0) {
|
||||
message.success(i18n.t('PROCMGR.KILL_PROCESS_SUCCESSFULLY'));
|
||||
tableRef.current.reload();
|
||||
}
|
||||
});
|
||||
}
|
||||
function killProcess(pid) {
|
||||
request(`/api/device/process/kill`, {pid: pid, device: props.device}).then(res => {
|
||||
let data = res.data;
|
||||
if (data.code === 0) {
|
||||
message.success(i18n.t('PROCMGR.KILL_PROCESS_SUCCESSFULLY'));
|
||||
tableRef.current.reload();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
async function getData(form) {
|
||||
await waitTime(300);
|
||||
let res = await request('/api/device/process/list', {device: props.device});
|
||||
setLoading(false);
|
||||
let data = res.data;
|
||||
if (data.code === 0) {
|
||||
data.data.processes = data.data.processes.sort((first, second) => (second.pid - first.pid));
|
||||
return ({
|
||||
data: data.data.processes,
|
||||
success: true,
|
||||
total: data.data.processes.length
|
||||
});
|
||||
}
|
||||
return ({data: [], success: false, total: 0});
|
||||
}
|
||||
async function getData(form) {
|
||||
await waitTime(300);
|
||||
let res = await request('/api/device/process/list', {device: props.device});
|
||||
setLoading(false);
|
||||
let data = res.data;
|
||||
if (data.code === 0) {
|
||||
data.data.processes = data.data.processes.sort((first, second) => (second.pid - first.pid));
|
||||
return ({
|
||||
data: data.data.processes,
|
||||
success: true,
|
||||
total: data.data.processes.length
|
||||
});
|
||||
}
|
||||
return ({data: [], success: false, total: 0});
|
||||
}
|
||||
|
||||
return (
|
||||
<DraggableModal
|
||||
draggable={true}
|
||||
maskClosable={false}
|
||||
destroyOnClose={true}
|
||||
modalTitle={i18n.t('PROCMGR.TITLE')}
|
||||
footer={null}
|
||||
width={400}
|
||||
bodyStyle={{
|
||||
padding: 0
|
||||
}}
|
||||
{...props}
|
||||
>
|
||||
<ProTable
|
||||
rowKey='pid'
|
||||
tableStyle={{
|
||||
paddingTop: '20px',
|
||||
minHeight: '355px',
|
||||
maxHeight: '355px'
|
||||
}}
|
||||
scroll={{scrollToFirstRowOnChange: true, y: 300}}
|
||||
search={false}
|
||||
size='small'
|
||||
loading={loading}
|
||||
onLoadingChange={setLoading}
|
||||
options={options}
|
||||
columns={columns}
|
||||
request={getData}
|
||||
pagination={false}
|
||||
actionRef={tableRef}
|
||||
components={virtualTable}
|
||||
>
|
||||
</ProTable>
|
||||
<Button
|
||||
style={{right:'59px'}}
|
||||
className='header-button'
|
||||
icon={<ReloadOutlined />}
|
||||
onClick={() => {
|
||||
tableRef.current.reload();
|
||||
}}
|
||||
/>
|
||||
</DraggableModal>
|
||||
)
|
||||
return (
|
||||
<DraggableModal
|
||||
draggable={true}
|
||||
maskClosable={false}
|
||||
destroyOnClose={true}
|
||||
modalTitle={i18n.t('PROCMGR.TITLE')}
|
||||
footer={null}
|
||||
width={400}
|
||||
bodyStyle={{
|
||||
padding: 0
|
||||
}}
|
||||
{...props}
|
||||
>
|
||||
<ProTable
|
||||
rowKey='pid'
|
||||
tableStyle={{
|
||||
paddingTop: '20px',
|
||||
minHeight: '355px',
|
||||
maxHeight: '355px'
|
||||
}}
|
||||
scroll={{scrollToFirstRowOnChange: true, y: 300}}
|
||||
search={false}
|
||||
size='small'
|
||||
loading={loading}
|
||||
onLoadingChange={setLoading}
|
||||
options={options}
|
||||
columns={columns}
|
||||
request={getData}
|
||||
pagination={false}
|
||||
actionRef={tableRef}
|
||||
components={virtualTable}
|
||||
>
|
||||
</ProTable>
|
||||
<Button
|
||||
style={{right:'59px'}}
|
||||
className='header-button'
|
||||
icon={<ReloadOutlined />}
|
||||
onClick={() => {
|
||||
tableRef.current.reload();
|
||||
}}
|
||||
/>
|
||||
</DraggableModal>
|
||||
)
|
||||
}
|
||||
|
||||
export default ProcessMgr;
|
@@ -5,48 +5,48 @@ import i18n from "../locale/locale";
|
||||
import {message} from "antd";
|
||||
|
||||
function Runner(props) {
|
||||
async function onFinish(form) {
|
||||
form.device = props.device.id;
|
||||
let basePath = location.origin + location.pathname + 'api/device/';
|
||||
request(basePath + 'exec', form).then(res => {
|
||||
if (res.data.code === 0) {
|
||||
message.success(i18n.t('RUNNER.EXECUTION_SUCCESS'));
|
||||
}
|
||||
});
|
||||
}
|
||||
async function onFinish(form) {
|
||||
form.device = props.device.id;
|
||||
let basePath = location.origin + location.pathname + 'api/device/';
|
||||
request(basePath + 'exec', form).then(res => {
|
||||
if (res.data.code === 0) {
|
||||
message.success(i18n.t('RUNNER.EXECUTION_SUCCESS'));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
return (
|
||||
<ModalForm
|
||||
modalProps={{
|
||||
destroyOnClose: true,
|
||||
maskClosable: false,
|
||||
}}
|
||||
title={i18n.t('RUNNER.TITLE')}
|
||||
width={380}
|
||||
onFinish={onFinish}
|
||||
onVisibleChange={visible => {
|
||||
if (!visible) props.onCancel();
|
||||
}}
|
||||
submitter={{
|
||||
render: (_, elems) => elems.pop()
|
||||
}}
|
||||
{...props}
|
||||
>
|
||||
<ProFormText
|
||||
width="md"
|
||||
name="cmd"
|
||||
label={i18n.t('RUNNER.CMD_PLACEHOLDER')}
|
||||
rules={[{
|
||||
required: true
|
||||
}]}
|
||||
/>
|
||||
<ProFormText
|
||||
width="md"
|
||||
name="args"
|
||||
label={i18n.t('RUNNER.ARGS_PLACEHOLDER')}
|
||||
/>
|
||||
</ModalForm>
|
||||
)
|
||||
return (
|
||||
<ModalForm
|
||||
modalProps={{
|
||||
destroyOnClose: true,
|
||||
maskClosable: false,
|
||||
}}
|
||||
title={i18n.t('RUNNER.TITLE')}
|
||||
width={380}
|
||||
onFinish={onFinish}
|
||||
onVisibleChange={visible => {
|
||||
if (!visible) props.onCancel();
|
||||
}}
|
||||
submitter={{
|
||||
render: (_, elems) => elems.pop()
|
||||
}}
|
||||
{...props}
|
||||
>
|
||||
<ProFormText
|
||||
width="md"
|
||||
name="cmd"
|
||||
label={i18n.t('RUNNER.CMD_PLACEHOLDER')}
|
||||
rules={[{
|
||||
required: true
|
||||
}]}
|
||||
/>
|
||||
<ProFormText
|
||||
width="md"
|
||||
name="args"
|
||||
label={i18n.t('RUNNER.ARGS_PLACEHOLDER')}
|
||||
/>
|
||||
</ModalForm>
|
||||
)
|
||||
}
|
||||
|
||||
export default Runner;
|
@@ -19,458 +19,458 @@ let ctrl = false;
|
||||
let conn = false;
|
||||
let ticker = 0;
|
||||
function TerminalModal(props) {
|
||||
let os = props.device.os;
|
||||
let extKeyRef = createRef();
|
||||
let secret = CryptoJS.enc.Hex.parse(genRandHex(32));
|
||||
let termRef = useCallback(e => {
|
||||
if (e !== null) {
|
||||
termRef.current = e;
|
||||
if (props.visible) {
|
||||
ctrl = false;
|
||||
term = new Terminal({
|
||||
convertEol: true,
|
||||
allowTransparency: false,
|
||||
cursorBlink: true,
|
||||
cursorStyle: "block",
|
||||
fontFamily: "Hack, monospace",
|
||||
fontSize: 16,
|
||||
logLevel: "off",
|
||||
})
|
||||
fit = new FitAddon();
|
||||
termEv = initialize(null);
|
||||
term.loadAddon(fit);
|
||||
term.loadAddon(new WebLinksAddon());
|
||||
term.open(termRef.current);
|
||||
fit.fit();
|
||||
term.clear();
|
||||
window.onresize = onResize;
|
||||
ticker = setInterval(() => {
|
||||
if (conn) sendData({act: 'PING'});
|
||||
}, 10000);
|
||||
term.focus();
|
||||
doResize();
|
||||
}
|
||||
}
|
||||
}, [props.visible]);
|
||||
let os = props.device.os;
|
||||
let extKeyRef = createRef();
|
||||
let secret = CryptoJS.enc.Hex.parse(genRandHex(32));
|
||||
let termRef = useCallback(e => {
|
||||
if (e !== null) {
|
||||
termRef.current = e;
|
||||
if (props.visible) {
|
||||
ctrl = false;
|
||||
term = new Terminal({
|
||||
convertEol: true,
|
||||
allowTransparency: false,
|
||||
cursorBlink: true,
|
||||
cursorStyle: "block",
|
||||
fontFamily: "Hack, monospace",
|
||||
fontSize: 16,
|
||||
logLevel: "off",
|
||||
})
|
||||
fit = new FitAddon();
|
||||
termEv = initialize(null);
|
||||
term.loadAddon(fit);
|
||||
term.loadAddon(new WebLinksAddon());
|
||||
term.open(termRef.current);
|
||||
fit.fit();
|
||||
term.clear();
|
||||
window.onresize = onResize;
|
||||
ticker = setInterval(() => {
|
||||
if (conn) sendData({act: 'PING'});
|
||||
}, 10000);
|
||||
term.focus();
|
||||
doResize();
|
||||
}
|
||||
}
|
||||
}, [props.visible]);
|
||||
|
||||
function afterClose() {
|
||||
clearInterval(ticker);
|
||||
if (conn) {
|
||||
sendData({act: 'TERMINAL_KILL'});
|
||||
ws.onclose = null;
|
||||
ws.close();
|
||||
}
|
||||
termEv?.dispose();
|
||||
termEv = null;
|
||||
fit?.dispose();
|
||||
fit = null;
|
||||
term?.dispose();
|
||||
term = null;
|
||||
ws = null;
|
||||
conn = false;
|
||||
}
|
||||
function afterClose() {
|
||||
clearInterval(ticker);
|
||||
if (conn) {
|
||||
sendData({act: 'TERMINAL_KILL'});
|
||||
ws.onclose = null;
|
||||
ws.close();
|
||||
}
|
||||
termEv?.dispose();
|
||||
termEv = null;
|
||||
fit?.dispose();
|
||||
fit = null;
|
||||
term?.dispose();
|
||||
term = null;
|
||||
ws = null;
|
||||
conn = false;
|
||||
}
|
||||
|
||||
function initialize(ev) {
|
||||
ev?.dispose();
|
||||
let buffer = { content: '', output: '' };
|
||||
let termEv = null;
|
||||
// Windows doesn't support pty, so we still use traditional way.
|
||||
// And we need to handle arrow events manually.
|
||||
if (os === 'windows') {
|
||||
termEv = term.onData(onWindowsInput.call(this, buffer));
|
||||
} else {
|
||||
termEv = term.onData(onUnixOSInput.call(this, buffer));
|
||||
}
|
||||
function initialize(ev) {
|
||||
ev?.dispose();
|
||||
let buffer = { content: '', output: '' };
|
||||
let termEv = null;
|
||||
// Windows doesn't support pty, so we still use traditional way.
|
||||
// And we need to handle arrow events manually.
|
||||
if (os === 'windows') {
|
||||
termEv = term.onData(onWindowsInput.call(this, buffer));
|
||||
} else {
|
||||
termEv = term.onData(onUnixOSInput.call(this, buffer));
|
||||
}
|
||||
|
||||
ws = new WebSocket(getBaseURL(true, `api/device/terminal?device=${props.device.id}&secret=${secret}`));
|
||||
ws.binaryType = 'arraybuffer';
|
||||
ws.onopen = () => {
|
||||
conn = true;
|
||||
}
|
||||
ws.onmessage = (e) => {
|
||||
let data = decrypt(e.data, secret);
|
||||
try {
|
||||
data = JSON.parse(data);
|
||||
} catch (_) {}
|
||||
if (conn) {
|
||||
if (data?.act === 'TERMINAL_OUTPUT') {
|
||||
data = ab2str(hex2buf(data?.data?.output));
|
||||
if (buffer.output.length > 0) {
|
||||
data = buffer.output + data;
|
||||
buffer.output = '';
|
||||
}
|
||||
if (buffer.content.length > 0) {
|
||||
if (data.length > buffer.content.length) {
|
||||
if (data.startsWith(buffer.content)) {
|
||||
data = data.substring(buffer.content.length);
|
||||
buffer.content = '';
|
||||
}
|
||||
} else {
|
||||
buffer.output = data;
|
||||
return;
|
||||
}
|
||||
}
|
||||
term.write(data);
|
||||
return;
|
||||
}
|
||||
if (data?.act === 'WARN') {
|
||||
message.warn(data.msg ? translate(data.msg) : i18n.t('COMMON.UNKNOWN_ERROR'));
|
||||
}
|
||||
}
|
||||
}
|
||||
ws.onclose = (e) => {
|
||||
if (conn) {
|
||||
conn = false;
|
||||
term.write(`\n${i18n.t('COMMON.DISCONNECTED')}\n`);
|
||||
secret = CryptoJS.enc.Hex.parse(genRandHex(32));
|
||||
}
|
||||
}
|
||||
ws.onerror = (e) => {
|
||||
console.error(e);
|
||||
if (conn) {
|
||||
conn = false;
|
||||
term.write(`\n${i18n.t('COMMON.DISCONNECTED')}\n`);
|
||||
secret = CryptoJS.enc.Hex.parse(genRandHex(32));
|
||||
} else {
|
||||
term.write(`\n${i18n.t('COMMON.CONNECTION_FAILED')}\n`);
|
||||
}
|
||||
}
|
||||
return termEv;
|
||||
}
|
||||
function onWindowsInput(buffer) {
|
||||
let cmd = '';
|
||||
let index = 0;
|
||||
let cursor = 0;
|
||||
let history = [];
|
||||
let tempCmd = '';
|
||||
let tempCursor = 0;
|
||||
function clearTerm() {
|
||||
let before = cmd.substring(0, cursor);
|
||||
let after = cmd.substring(cursor);
|
||||
term.write('\b'.repeat(wcwidth(before)));
|
||||
term.write(' '.repeat(wcwidth(cmd)));
|
||||
term.write('\b'.repeat(wcwidth(cmd)));
|
||||
}
|
||||
return function (e) {
|
||||
if (!conn) {
|
||||
if (e === '\r' || e === '\n' || e === ' ') {
|
||||
term.write(`\n${i18n.t('COMMON.RECONNECTING')}\n`);
|
||||
termEv = initialize(termEv);
|
||||
}
|
||||
return;
|
||||
}
|
||||
switch (e) {
|
||||
case '\x1B\x5B\x41': // up arrow.
|
||||
if (index > 0 && index <= history.length) {
|
||||
if (index === history.length) {
|
||||
tempCmd = cmd;
|
||||
tempCursor = cursor;
|
||||
}
|
||||
index--;
|
||||
clearTerm.call(this);
|
||||
cmd = history[index];
|
||||
cursor = cmd.length;
|
||||
term.write(cmd);
|
||||
}
|
||||
break;
|
||||
case '\x1B\x5B\x42': // down arrow.
|
||||
if (index + 1 < history.length) {
|
||||
index++;
|
||||
clearTerm.call(this);
|
||||
cmd = history[index];
|
||||
cursor = cmd.length;
|
||||
term.write(cmd);
|
||||
} else if (index + 1 <= history.length) {
|
||||
clearTerm.call(this);
|
||||
index++;
|
||||
cmd = tempCmd;
|
||||
cursor = tempCursor;
|
||||
term.write(cmd);
|
||||
term.write('\x1B\x5B\x44'.repeat(wcwidth(cmd.substring(cursor))));
|
||||
tempCmd = '';
|
||||
tempCursor = 0;
|
||||
}
|
||||
break;
|
||||
case '\x1B\x5B\x43': // right arrow.
|
||||
if (cursor < cmd.length) {
|
||||
term.write('\x1B\x5B\x43'.repeat(wcwidth(cmd[cursor])));
|
||||
cursor++;
|
||||
}
|
||||
break;
|
||||
case '\x1B\x5B\x44': // left arrow.
|
||||
if (cursor > 0) {
|
||||
term.write('\x1B\x5B\x44'.repeat(wcwidth(cmd[cursor-1])));
|
||||
cursor--;
|
||||
}
|
||||
break;
|
||||
case '\r':
|
||||
case '\n':
|
||||
if (cmd === 'clear' || cmd === 'cls') {
|
||||
clearTerm.call(this);
|
||||
term.clear();
|
||||
} else {
|
||||
term.write('\n');
|
||||
sendWindowsInput(cmd + '\n');
|
||||
buffer.content = cmd + '\n';
|
||||
}
|
||||
if (cmd.length > 0) history.push(cmd);
|
||||
cursor = 0;
|
||||
cmd = '';
|
||||
if (history.length > 128) {
|
||||
history = history.slice(history.length - 128);
|
||||
}
|
||||
tempCmd = '';
|
||||
tempCursor = 0;
|
||||
index = history.length;
|
||||
break;
|
||||
case '\x7F': // backspace.
|
||||
if (cmd.length > 0 && cursor > 0) {
|
||||
cursor--;
|
||||
let charWidth = wcwidth(cmd[cursor]);
|
||||
let before = cmd.substring(0, cursor);
|
||||
let after = cmd.substring(cursor+1);
|
||||
cmd = before + after;
|
||||
term.write('\b'.repeat(charWidth));
|
||||
term.write(after + ' '.repeat(charWidth));
|
||||
term.write('\x1B\x5B\x44'.repeat(wcwidth(after) + charWidth));
|
||||
}
|
||||
break;
|
||||
default:
|
||||
if ((e >= String.fromCharCode(0x20) && e <= String.fromCharCode(0x7B)) || e >= '\xA0') {
|
||||
if (cursor < cmd.length) {
|
||||
let before = cmd.substring(0, cursor);
|
||||
let after = cmd.substring(cursor);
|
||||
cmd = before + e + after;
|
||||
term.write(e + after);
|
||||
term.write('\x1B\x5B\x44'.repeat(wcwidth(after)));
|
||||
} else {
|
||||
cmd += e;
|
||||
term.write(e);
|
||||
}
|
||||
cursor += e.length;
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
function onUnixOSInput(_) {
|
||||
return function (e) {
|
||||
if (!conn) {
|
||||
if (e === '\r' || e === ' ') {
|
||||
term.write(`\n${i18n.t('COMMON.RECONNECTING')}\n`);
|
||||
termEv = initialize(termEv);
|
||||
}
|
||||
return;
|
||||
}
|
||||
sendUnixOSInput(e);
|
||||
};
|
||||
}
|
||||
ws = new WebSocket(getBaseURL(true, `api/device/terminal?device=${props.device.id}&secret=${secret}`));
|
||||
ws.binaryType = 'arraybuffer';
|
||||
ws.onopen = () => {
|
||||
conn = true;
|
||||
}
|
||||
ws.onmessage = (e) => {
|
||||
let data = decrypt(e.data, secret);
|
||||
try {
|
||||
data = JSON.parse(data);
|
||||
} catch (_) {}
|
||||
if (conn) {
|
||||
if (data?.act === 'TERMINAL_OUTPUT') {
|
||||
data = ab2str(hex2buf(data?.data?.output));
|
||||
if (buffer.output.length > 0) {
|
||||
data = buffer.output + data;
|
||||
buffer.output = '';
|
||||
}
|
||||
if (buffer.content.length > 0) {
|
||||
if (data.length > buffer.content.length) {
|
||||
if (data.startsWith(buffer.content)) {
|
||||
data = data.substring(buffer.content.length);
|
||||
buffer.content = '';
|
||||
}
|
||||
} else {
|
||||
buffer.output = data;
|
||||
return;
|
||||
}
|
||||
}
|
||||
term.write(data);
|
||||
return;
|
||||
}
|
||||
if (data?.act === 'WARN') {
|
||||
message.warn(data.msg ? translate(data.msg) : i18n.t('COMMON.UNKNOWN_ERROR'));
|
||||
}
|
||||
}
|
||||
}
|
||||
ws.onclose = (e) => {
|
||||
if (conn) {
|
||||
conn = false;
|
||||
term.write(`\n${i18n.t('COMMON.DISCONNECTED')}\n`);
|
||||
secret = CryptoJS.enc.Hex.parse(genRandHex(32));
|
||||
}
|
||||
}
|
||||
ws.onerror = (e) => {
|
||||
console.error(e);
|
||||
if (conn) {
|
||||
conn = false;
|
||||
term.write(`\n${i18n.t('COMMON.DISCONNECTED')}\n`);
|
||||
secret = CryptoJS.enc.Hex.parse(genRandHex(32));
|
||||
} else {
|
||||
term.write(`\n${i18n.t('COMMON.CONNECTION_FAILED')}\n`);
|
||||
}
|
||||
}
|
||||
return termEv;
|
||||
}
|
||||
function onWindowsInput(buffer) {
|
||||
let cmd = '';
|
||||
let index = 0;
|
||||
let cursor = 0;
|
||||
let history = [];
|
||||
let tempCmd = '';
|
||||
let tempCursor = 0;
|
||||
function clearTerm() {
|
||||
let before = cmd.substring(0, cursor);
|
||||
let after = cmd.substring(cursor);
|
||||
term.write('\b'.repeat(wcwidth(before)));
|
||||
term.write(' '.repeat(wcwidth(cmd)));
|
||||
term.write('\b'.repeat(wcwidth(cmd)));
|
||||
}
|
||||
return function (e) {
|
||||
if (!conn) {
|
||||
if (e === '\r' || e === '\n' || e === ' ') {
|
||||
term.write(`\n${i18n.t('COMMON.RECONNECTING')}\n`);
|
||||
termEv = initialize(termEv);
|
||||
}
|
||||
return;
|
||||
}
|
||||
switch (e) {
|
||||
case '\x1B\x5B\x41': // up arrow.
|
||||
if (index > 0 && index <= history.length) {
|
||||
if (index === history.length) {
|
||||
tempCmd = cmd;
|
||||
tempCursor = cursor;
|
||||
}
|
||||
index--;
|
||||
clearTerm.call(this);
|
||||
cmd = history[index];
|
||||
cursor = cmd.length;
|
||||
term.write(cmd);
|
||||
}
|
||||
break;
|
||||
case '\x1B\x5B\x42': // down arrow.
|
||||
if (index + 1 < history.length) {
|
||||
index++;
|
||||
clearTerm.call(this);
|
||||
cmd = history[index];
|
||||
cursor = cmd.length;
|
||||
term.write(cmd);
|
||||
} else if (index + 1 <= history.length) {
|
||||
clearTerm.call(this);
|
||||
index++;
|
||||
cmd = tempCmd;
|
||||
cursor = tempCursor;
|
||||
term.write(cmd);
|
||||
term.write('\x1B\x5B\x44'.repeat(wcwidth(cmd.substring(cursor))));
|
||||
tempCmd = '';
|
||||
tempCursor = 0;
|
||||
}
|
||||
break;
|
||||
case '\x1B\x5B\x43': // right arrow.
|
||||
if (cursor < cmd.length) {
|
||||
term.write('\x1B\x5B\x43'.repeat(wcwidth(cmd[cursor])));
|
||||
cursor++;
|
||||
}
|
||||
break;
|
||||
case '\x1B\x5B\x44': // left arrow.
|
||||
if (cursor > 0) {
|
||||
term.write('\x1B\x5B\x44'.repeat(wcwidth(cmd[cursor-1])));
|
||||
cursor--;
|
||||
}
|
||||
break;
|
||||
case '\r':
|
||||
case '\n':
|
||||
if (cmd === 'clear' || cmd === 'cls') {
|
||||
clearTerm.call(this);
|
||||
term.clear();
|
||||
} else {
|
||||
term.write('\n');
|
||||
sendWindowsInput(cmd + '\n');
|
||||
buffer.content = cmd + '\n';
|
||||
}
|
||||
if (cmd.length > 0) history.push(cmd);
|
||||
cursor = 0;
|
||||
cmd = '';
|
||||
if (history.length > 128) {
|
||||
history = history.slice(history.length - 128);
|
||||
}
|
||||
tempCmd = '';
|
||||
tempCursor = 0;
|
||||
index = history.length;
|
||||
break;
|
||||
case '\x7F': // backspace.
|
||||
if (cmd.length > 0 && cursor > 0) {
|
||||
cursor--;
|
||||
let charWidth = wcwidth(cmd[cursor]);
|
||||
let before = cmd.substring(0, cursor);
|
||||
let after = cmd.substring(cursor+1);
|
||||
cmd = before + after;
|
||||
term.write('\b'.repeat(charWidth));
|
||||
term.write(after + ' '.repeat(charWidth));
|
||||
term.write('\x1B\x5B\x44'.repeat(wcwidth(after) + charWidth));
|
||||
}
|
||||
break;
|
||||
default:
|
||||
if ((e >= String.fromCharCode(0x20) && e <= String.fromCharCode(0x7B)) || e >= '\xA0') {
|
||||
if (cursor < cmd.length) {
|
||||
let before = cmd.substring(0, cursor);
|
||||
let after = cmd.substring(cursor);
|
||||
cmd = before + e + after;
|
||||
term.write(e + after);
|
||||
term.write('\x1B\x5B\x44'.repeat(wcwidth(after)));
|
||||
} else {
|
||||
cmd += e;
|
||||
term.write(e);
|
||||
}
|
||||
cursor += e.length;
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
function onUnixOSInput(_) {
|
||||
return function (e) {
|
||||
if (!conn) {
|
||||
if (e === '\r' || e === ' ') {
|
||||
term.write(`\n${i18n.t('COMMON.RECONNECTING')}\n`);
|
||||
termEv = initialize(termEv);
|
||||
}
|
||||
return;
|
||||
}
|
||||
sendUnixOSInput(e);
|
||||
};
|
||||
}
|
||||
|
||||
function sendWindowsInput(input) {
|
||||
if (conn) {
|
||||
sendData({
|
||||
act: 'TERMINAL_INPUT',
|
||||
data: {
|
||||
input: CryptoJS.enc.Hex.stringify(CryptoJS.enc.Utf8.parse(input))
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
function sendUnixOSInput(input) {
|
||||
if (conn) {
|
||||
if (ctrl && input.length === 1) {
|
||||
let charCode = input.charCodeAt(0);
|
||||
if (charCode >= 0x61 && charCode <= 0x7A) {
|
||||
charCode -= 0x60;
|
||||
ctrl = false;
|
||||
extKeyRef.current.setCtrl(false);
|
||||
} else if (charCode >= 0x40 && charCode <= 0x5F) {
|
||||
charCode -= 0x40;
|
||||
ctrl = false;
|
||||
extKeyRef.current.setCtrl(false);
|
||||
}
|
||||
input = String.fromCharCode(charCode);
|
||||
}
|
||||
console.log(CryptoJS.enc.Hex.stringify(CryptoJS.enc.Utf8.parse(input)));
|
||||
sendData({
|
||||
act: 'TERMINAL_INPUT',
|
||||
data: {
|
||||
input: CryptoJS.enc.Hex.stringify(CryptoJS.enc.Utf8.parse(input))
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
function sendData(data) {
|
||||
if (conn) {
|
||||
ws.send(encrypt(data, secret));
|
||||
}
|
||||
}
|
||||
function sendWindowsInput(input) {
|
||||
if (conn) {
|
||||
sendData({
|
||||
act: 'TERMINAL_INPUT',
|
||||
data: {
|
||||
input: CryptoJS.enc.Hex.stringify(CryptoJS.enc.Utf8.parse(input))
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
function sendUnixOSInput(input) {
|
||||
if (conn) {
|
||||
if (ctrl && input.length === 1) {
|
||||
let charCode = input.charCodeAt(0);
|
||||
if (charCode >= 0x61 && charCode <= 0x7A) {
|
||||
charCode -= 0x60;
|
||||
ctrl = false;
|
||||
extKeyRef.current.setCtrl(false);
|
||||
} else if (charCode >= 0x40 && charCode <= 0x5F) {
|
||||
charCode -= 0x40;
|
||||
ctrl = false;
|
||||
extKeyRef.current.setCtrl(false);
|
||||
}
|
||||
input = String.fromCharCode(charCode);
|
||||
}
|
||||
console.log(CryptoJS.enc.Hex.stringify(CryptoJS.enc.Utf8.parse(input)));
|
||||
sendData({
|
||||
act: 'TERMINAL_INPUT',
|
||||
data: {
|
||||
input: CryptoJS.enc.Hex.stringify(CryptoJS.enc.Utf8.parse(input))
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
function sendData(data) {
|
||||
if (conn) {
|
||||
ws.send(encrypt(data, secret));
|
||||
}
|
||||
}
|
||||
|
||||
function doResize() {
|
||||
let height = document.body.clientHeight;
|
||||
let rows = Math.floor(height / 42);
|
||||
let cols = term?.cols;
|
||||
fit?.fit?.();
|
||||
term?.resize?.(cols, rows);
|
||||
term?.scrollToBottom?.();
|
||||
function doResize() {
|
||||
let height = document.body.clientHeight;
|
||||
let rows = Math.floor(height / 42);
|
||||
let cols = term?.cols;
|
||||
fit?.fit?.();
|
||||
term?.resize?.(cols, rows);
|
||||
term?.scrollToBottom?.();
|
||||
|
||||
if (conn) {
|
||||
sendData({
|
||||
act: 'TERMINAL_RESIZE',
|
||||
data: {
|
||||
width: cols,
|
||||
height: rows
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
function onResize() {
|
||||
if (typeof doResize === 'function') {
|
||||
debounce(doResize, 70);
|
||||
}
|
||||
}
|
||||
if (conn) {
|
||||
sendData({
|
||||
act: 'TERMINAL_RESIZE',
|
||||
data: {
|
||||
width: cols,
|
||||
height: rows
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
function onResize() {
|
||||
if (typeof doResize === 'function') {
|
||||
debounce(doResize, 70);
|
||||
}
|
||||
}
|
||||
|
||||
function onCtrl(val) {
|
||||
term?.focus?.();
|
||||
if (!conn && val) {
|
||||
extKeyRef.current.setCtrl(false);
|
||||
return;
|
||||
}
|
||||
ctrl = val;
|
||||
}
|
||||
function onExtKey(val, focus) {
|
||||
sendUnixOSInput(val);
|
||||
if (focus) term?.focus?.();
|
||||
}
|
||||
function onCtrl(val) {
|
||||
term?.focus?.();
|
||||
if (!conn && val) {
|
||||
extKeyRef.current.setCtrl(false);
|
||||
return;
|
||||
}
|
||||
ctrl = val;
|
||||
}
|
||||
function onExtKey(val, focus) {
|
||||
sendUnixOSInput(val);
|
||||
if (focus) term?.focus?.();
|
||||
}
|
||||
|
||||
return (
|
||||
<DraggableModal
|
||||
draggable={true}
|
||||
maskClosable={false}
|
||||
modalTitle={i18n.t('TERMINAL.TITLE')}
|
||||
visible={props.visible}
|
||||
onCancel={props.onCancel}
|
||||
bodyStyle={{padding: 12}}
|
||||
afterClose={afterClose}
|
||||
destroyOnClose={true}
|
||||
footer={null}
|
||||
height={200}
|
||||
width={900}
|
||||
>
|
||||
<ExtKeyboard
|
||||
ref={extKeyRef}
|
||||
onCtrl={onCtrl}
|
||||
onExtKey={onExtKey}
|
||||
visible={os!=='windows'}
|
||||
/>
|
||||
<div
|
||||
style={{
|
||||
padding: '0 5px',
|
||||
backgroundColor: '#000',
|
||||
}}
|
||||
ref={termRef}
|
||||
/>
|
||||
</DraggableModal>
|
||||
)
|
||||
return (
|
||||
<DraggableModal
|
||||
draggable={true}
|
||||
maskClosable={false}
|
||||
modalTitle={i18n.t('TERMINAL.TITLE')}
|
||||
visible={props.visible}
|
||||
onCancel={props.onCancel}
|
||||
bodyStyle={{padding: 12}}
|
||||
afterClose={afterClose}
|
||||
destroyOnClose={true}
|
||||
footer={null}
|
||||
height={200}
|
||||
width={900}
|
||||
>
|
||||
<ExtKeyboard
|
||||
ref={extKeyRef}
|
||||
onCtrl={onCtrl}
|
||||
onExtKey={onExtKey}
|
||||
visible={os!=='windows'}
|
||||
/>
|
||||
<div
|
||||
style={{
|
||||
padding: '0 5px',
|
||||
backgroundColor: '#000',
|
||||
}}
|
||||
ref={termRef}
|
||||
/>
|
||||
</DraggableModal>
|
||||
)
|
||||
}
|
||||
|
||||
class ExtKeyboard extends React.Component {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
this.visible = props.visible;
|
||||
if (!this.visible) return;
|
||||
this.fnKeys = [
|
||||
{key: '\x1B\x4F\x50', label: 'F1'},
|
||||
{key: '\x1B\x4F\x51', label: 'F2'},
|
||||
{key: '\x1B\x4F\x52', label: 'F3'},
|
||||
{key: '\x1B\x4F\x53', label: 'F4'},
|
||||
{key: '\x1B\x5B\x31\x35\x7E', label: 'F5'},
|
||||
{key: '\x1B\x5B\x31\x37\x7E', label: 'F6'},
|
||||
{key: '\x1B\x5B\x31\x38\x7E', label: 'F7'},
|
||||
{key: '\x1B\x5B\x31\x39\x7E', label: 'F8'},
|
||||
{key: '\x1B\x5B\x32\x30\x7E', label: 'F9'},
|
||||
{key: '\x1B\x5B\x32\x31\x7E', label: 'F10'},
|
||||
{key: '\x1B\x5B\x32\x33\x7E', label: 'F11'},
|
||||
{key: '\x1B\x5B\x32\x34\x7E', label: 'F12'},
|
||||
];
|
||||
this.fnMenu = (
|
||||
<Menu onClick={this.onFnKey.bind(this)}>
|
||||
{this.fnKeys.map(e =>
|
||||
<Menu.Item key={e.key}>
|
||||
{e.label}
|
||||
</Menu.Item>
|
||||
)}
|
||||
</Menu>
|
||||
);
|
||||
this.state = {ctrl: false};
|
||||
}
|
||||
constructor(props) {
|
||||
super(props);
|
||||
this.visible = props.visible;
|
||||
if (!this.visible) return;
|
||||
this.fnKeys = [
|
||||
{key: '\x1B\x4F\x50', label: 'F1'},
|
||||
{key: '\x1B\x4F\x51', label: 'F2'},
|
||||
{key: '\x1B\x4F\x52', label: 'F3'},
|
||||
{key: '\x1B\x4F\x53', label: 'F4'},
|
||||
{key: '\x1B\x5B\x31\x35\x7E', label: 'F5'},
|
||||
{key: '\x1B\x5B\x31\x37\x7E', label: 'F6'},
|
||||
{key: '\x1B\x5B\x31\x38\x7E', label: 'F7'},
|
||||
{key: '\x1B\x5B\x31\x39\x7E', label: 'F8'},
|
||||
{key: '\x1B\x5B\x32\x30\x7E', label: 'F9'},
|
||||
{key: '\x1B\x5B\x32\x31\x7E', label: 'F10'},
|
||||
{key: '\x1B\x5B\x32\x33\x7E', label: 'F11'},
|
||||
{key: '\x1B\x5B\x32\x34\x7E', label: 'F12'},
|
||||
];
|
||||
this.fnMenu = (
|
||||
<Menu onClick={this.onFnKey.bind(this)}>
|
||||
{this.fnKeys.map(e =>
|
||||
<Menu.Item key={e.key}>
|
||||
{e.label}
|
||||
</Menu.Item>
|
||||
)}
|
||||
</Menu>
|
||||
);
|
||||
this.state = {ctrl: false};
|
||||
}
|
||||
|
||||
onCtrl() {
|
||||
this.setState({ctrl: !this.state.ctrl});
|
||||
this.props.onCtrl(!this.state.ctrl);
|
||||
}
|
||||
onExtKey(key) {
|
||||
this.props.onExtKey(key, true);
|
||||
}
|
||||
onFnKey(e) {
|
||||
this.props.onExtKey(e.key, false);
|
||||
}
|
||||
onCtrl() {
|
||||
this.setState({ctrl: !this.state.ctrl});
|
||||
this.props.onCtrl(!this.state.ctrl);
|
||||
}
|
||||
onExtKey(key) {
|
||||
this.props.onExtKey(key, true);
|
||||
}
|
||||
onFnKey(e) {
|
||||
this.props.onExtKey(e.key, false);
|
||||
}
|
||||
|
||||
setCtrl(val) {
|
||||
this.setState({ctrl: val});
|
||||
}
|
||||
setCtrl(val) {
|
||||
this.setState({ctrl: val});
|
||||
}
|
||||
|
||||
render() {
|
||||
if (!this.visible) return null;
|
||||
return (
|
||||
<Space style={{paddingBottom: 12}}>
|
||||
<>
|
||||
<Button
|
||||
type={this.state.ctrl?'primary':'default'}
|
||||
onClick={this.onCtrl.bind(this)}
|
||||
>
|
||||
CTRL
|
||||
</Button>
|
||||
<Button
|
||||
onClick={this.onExtKey.bind(this, '\x1B')}
|
||||
>
|
||||
ESC
|
||||
</Button>
|
||||
<Button
|
||||
onClick={this.onExtKey.bind(this, '\x09')}
|
||||
>
|
||||
TAB
|
||||
</Button>
|
||||
</>
|
||||
<>
|
||||
<Button
|
||||
onClick={this.onExtKey.bind(this, '\x1B\x5B\x41')}
|
||||
>
|
||||
⬆
|
||||
</Button>
|
||||
<Button
|
||||
onClick={this.onExtKey.bind(this, '\x1B\x5B\x42')}
|
||||
>
|
||||
⬇
|
||||
</Button>
|
||||
<Button
|
||||
onClick={this.onExtKey.bind(this, '\x1B\x5B\x43')}
|
||||
>
|
||||
➡
|
||||
</Button>
|
||||
<Button
|
||||
onClick={this.onExtKey.bind(this, '\x1B\x5B\x44')}
|
||||
>
|
||||
⬅
|
||||
</Button>
|
||||
</>
|
||||
<Dropdown.Button
|
||||
overlay={this.fnMenu}
|
||||
>
|
||||
{i18n.t('TERMINAL.FUNCTION_KEYS')}
|
||||
</Dropdown.Button>
|
||||
</Space>
|
||||
);
|
||||
}
|
||||
render() {
|
||||
if (!this.visible) return null;
|
||||
return (
|
||||
<Space style={{paddingBottom: 12}}>
|
||||
<>
|
||||
<Button
|
||||
type={this.state.ctrl?'primary':'default'}
|
||||
onClick={this.onCtrl.bind(this)}
|
||||
>
|
||||
CTRL
|
||||
</Button>
|
||||
<Button
|
||||
onClick={this.onExtKey.bind(this, '\x1B')}
|
||||
>
|
||||
ESC
|
||||
</Button>
|
||||
<Button
|
||||
onClick={this.onExtKey.bind(this, '\x09')}
|
||||
>
|
||||
TAB
|
||||
</Button>
|
||||
</>
|
||||
<>
|
||||
<Button
|
||||
onClick={this.onExtKey.bind(this, '\x1B\x5B\x41')}
|
||||
>
|
||||
⬆
|
||||
</Button>
|
||||
<Button
|
||||
onClick={this.onExtKey.bind(this, '\x1B\x5B\x42')}
|
||||
>
|
||||
⬇
|
||||
</Button>
|
||||
<Button
|
||||
onClick={this.onExtKey.bind(this, '\x1B\x5B\x43')}
|
||||
>
|
||||
➡
|
||||
</Button>
|
||||
<Button
|
||||
onClick={this.onExtKey.bind(this, '\x1B\x5B\x44')}
|
||||
>
|
||||
⬅
|
||||
</Button>
|
||||
</>
|
||||
<Dropdown.Button
|
||||
overlay={this.fnMenu}
|
||||
>
|
||||
{i18n.t('TERMINAL.FUNCTION_KEYS')}
|
||||
</Dropdown.Button>
|
||||
</Space>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export default TerminalModal;
|
@@ -1,19 +1,19 @@
|
||||
.ant-page-header {
|
||||
display: none;
|
||||
display: none;
|
||||
}
|
||||
|
||||
.ant-pro-top-nav-header-logo {
|
||||
user-select: none;
|
||||
user-select: none;
|
||||
}
|
||||
|
||||
.header-button {
|
||||
background: transparent !important;
|
||||
position: absolute !important;
|
||||
font-weight: 700 !important;
|
||||
cursor: pointer !important;
|
||||
border: none !important;
|
||||
top: 3px !important;
|
||||
height: 50px !important;
|
||||
width: 50px !important;
|
||||
color: rgba(0, 0, 0, 0.45) !important;
|
||||
background: transparent !important;
|
||||
position: absolute !important;
|
||||
font-weight: 700 !important;
|
||||
cursor: pointer !important;
|
||||
border: none !important;
|
||||
top: 3px !important;
|
||||
height: 50px !important;
|
||||
width: 50px !important;
|
||||
color: rgba(0, 0, 0, 0.45) !important;
|
||||
}
|
@@ -7,35 +7,36 @@ import {ConfigProvider} from "antd";
|
||||
import './wrapper.css';
|
||||
|
||||
function wrapper(props) {
|
||||
return (
|
||||
<ProLayout
|
||||
loading={false}
|
||||
title='Spark'
|
||||
layout='top'
|
||||
navTheme='light'
|
||||
collapsed={true}
|
||||
fixedHeader={true}
|
||||
contentWidth='fluid'
|
||||
collapsedButtonRender={Title}
|
||||
>
|
||||
<PageContainer>
|
||||
<ConfigProvider locale={getLang()==='zh-CN'?zhCN:en}>
|
||||
{props.children}
|
||||
</ConfigProvider>
|
||||
</PageContainer>
|
||||
</ProLayout>
|
||||
);
|
||||
};
|
||||
return (
|
||||
<ProLayout
|
||||
loading={false}
|
||||
title='Spark'
|
||||
logo={null}
|
||||
layout='top'
|
||||
navTheme='light'
|
||||
collapsed={true}
|
||||
fixedHeader={true}
|
||||
contentWidth='fluid'
|
||||
collapsedButtonRender={Title}
|
||||
>
|
||||
<PageContainer>
|
||||
<ConfigProvider locale={getLang()==='zh-CN'?zhCN:en}>
|
||||
{props.children}
|
||||
</ConfigProvider>
|
||||
</PageContainer>
|
||||
</ProLayout>
|
||||
);
|
||||
}
|
||||
function Title() {
|
||||
return (
|
||||
<div
|
||||
style={{
|
||||
userSelect: 'none',
|
||||
fontWeight: 500
|
||||
}}
|
||||
>
|
||||
Spark
|
||||
</div>
|
||||
)
|
||||
return (
|
||||
<div
|
||||
style={{
|
||||
userSelect: 'none',
|
||||
fontWeight: 500
|
||||
}}
|
||||
>
|
||||
Spark
|
||||
</div>
|
||||
)
|
||||
}
|
||||
export default wrapper;
|
@@ -1,11 +0,0 @@
|
||||
{
|
||||
"mac": {
|
||||
"show": true
|
||||
},
|
||||
"ram_total": {
|
||||
"show": true
|
||||
},
|
||||
"disk_usage": {
|
||||
"show": true
|
||||
}
|
||||
}
|
@@ -1,16 +1,16 @@
|
||||
body {
|
||||
overflow-x: hidden;
|
||||
overflow-x: hidden;
|
||||
}
|
||||
|
||||
#root {
|
||||
height: 100%;
|
||||
background-image: url('static/bg.svg');
|
||||
background-repeat: no-repeat;
|
||||
background-position: center 110px;
|
||||
background-color: #f0f2f5;
|
||||
background-size: 100%;
|
||||
height: 100%;
|
||||
background-image: url('static/bg.svg');
|
||||
background-repeat: no-repeat;
|
||||
background-position: center 110px;
|
||||
background-color: #f0f2f5;
|
||||
background-size: 100%;
|
||||
}
|
||||
|
||||
.ant-table-cell {
|
||||
border: none !important;
|
||||
border: none !important;
|
||||
}
|
@@ -14,41 +14,41 @@ import {translate} from "./utils/utils";
|
||||
|
||||
axios.defaults.baseURL = '.';
|
||||
axios.interceptors.response.use(async res => {
|
||||
let data = res.data;
|
||||
if (data.hasOwnProperty('code')) {
|
||||
if (data.code !== 0){
|
||||
message.warn(translate(data.msg));
|
||||
} else {
|
||||
// The first request will ask user to provide user/pass.
|
||||
// If set timeout at the beginning, then timeout warning
|
||||
// might be triggered before authentication finished.
|
||||
axios.defaults.timeout = 5000;
|
||||
}
|
||||
}
|
||||
return Promise.resolve(res);
|
||||
let data = res.data;
|
||||
if (data.hasOwnProperty('code')) {
|
||||
if (data.code !== 0){
|
||||
message.warn(translate(data.msg));
|
||||
} else {
|
||||
// The first request will ask user to provide user/pass.
|
||||
// If set timeout at the beginning, then timeout warning
|
||||
// might be triggered before authentication finished.
|
||||
axios.defaults.timeout = 5000;
|
||||
}
|
||||
}
|
||||
return Promise.resolve(res);
|
||||
}, err => {
|
||||
// console.error(err);
|
||||
if (err.code === 'ECONNABORTED') {
|
||||
message.error(i18n.t('COMMON.REQUEST_TIMEOUT'));
|
||||
return Promise.reject(err);
|
||||
}
|
||||
let res = err.response;
|
||||
let data = res?.data ?? {};
|
||||
if (data.hasOwnProperty('code') && data.hasOwnProperty('msg')) {
|
||||
if (data.code !== 0){
|
||||
message.warn(translate(data.msg));
|
||||
return Promise.resolve(res);
|
||||
}
|
||||
}
|
||||
return Promise.reject(err);
|
||||
// console.error(err);
|
||||
if (err.code === 'ECONNABORTED') {
|
||||
message.error(i18n.t('COMMON.REQUEST_TIMEOUT'));
|
||||
return Promise.reject(err);
|
||||
}
|
||||
let res = err.response;
|
||||
let data = res?.data ?? {};
|
||||
if (data.hasOwnProperty('code') && data.hasOwnProperty('msg')) {
|
||||
if (data.code !== 0){
|
||||
message.warn(translate(data.msg));
|
||||
return Promise.resolve(res);
|
||||
}
|
||||
}
|
||||
return Promise.reject(err);
|
||||
});
|
||||
|
||||
ReactDOM.render(
|
||||
<Router>
|
||||
<Routes>
|
||||
<Route path="/" element={<Wrapper><Overview/></Wrapper>}/>
|
||||
<Route path="*" element={<Err/>}/>
|
||||
</Routes>
|
||||
</Router>,
|
||||
document.getElementById('root')
|
||||
<Router>
|
||||
<Routes>
|
||||
<Route path="/" element={<Wrapper><Overview/></Wrapper>}/>
|
||||
<Route path="*" element={<Err/>}/>
|
||||
</Routes>
|
||||
</Router>,
|
||||
document.getElementById('root')
|
||||
);
|
@@ -1,28 +1,28 @@
|
||||
import i18n from 'i18next';
|
||||
|
||||
const locales = {
|
||||
'en': 'en',
|
||||
'en-US': 'en',
|
||||
'zh-CN': 'zh-CN',
|
||||
'en': 'en',
|
||||
'en-US': 'en',
|
||||
'zh-CN': 'zh-CN',
|
||||
};
|
||||
const lang = navigator.language && navigator.language.length ? navigator.language : 'en';
|
||||
|
||||
let resources = {};
|
||||
for (const locale in locales) {
|
||||
resources[locale] = {
|
||||
translation: require(`./${locales[locale]}.json`),
|
||||
};
|
||||
resources[locale] = {
|
||||
translation: require(`./${locales[locale]}.json`),
|
||||
};
|
||||
}
|
||||
|
||||
i18n.init({
|
||||
lng: lang,
|
||||
fallbackLng: 'en',
|
||||
initImmediate: true,
|
||||
resources
|
||||
lng: lang,
|
||||
fallbackLng: 'en',
|
||||
initImmediate: true,
|
||||
resources
|
||||
});
|
||||
|
||||
function getLang() {
|
||||
return lang;
|
||||
return lang;
|
||||
}
|
||||
|
||||
export { getLang };
|
||||
|
@@ -2,13 +2,13 @@ import React from 'react';
|
||||
import i18n from "../locale/locale";
|
||||
|
||||
export default function () {
|
||||
// setTimeout(()=>{
|
||||
// location.href = '#/';
|
||||
// }, 3000);
|
||||
// setTimeout(()=>{
|
||||
// location.href = '#/';
|
||||
// }, 3000);
|
||||
|
||||
return (
|
||||
<h1 style={{textAlign: 'center', userSelect: 'none'}}>
|
||||
{i18n.t('COMMON.PAGE_NOT_FOUND')}
|
||||
</h1>
|
||||
);
|
||||
return (
|
||||
<h1 style={{textAlign: 'center', userSelect: 'none'}}>
|
||||
{i18n.t('COMMON.PAGE_NOT_FOUND')}
|
||||
</h1>
|
||||
);
|
||||
}
|
@@ -11,453 +11,451 @@ import Runner from "../components/runner";
|
||||
import {QuestionCircleOutlined} from "@ant-design/icons";
|
||||
import i18n from "../locale/locale";
|
||||
|
||||
import defaultColumnsState from "../config/columnsState.json";
|
||||
|
||||
// DO NOT EDIT OR DELETE THIS COPYRIGHT MESSAGE.
|
||||
console.log("%c By XZB %c https://github.com/XZB-1248/Spark", 'font-family:"Helvetica Neue",Helvetica,Arial,sans-serif;font-size:64px;color:#00bbee;-webkit-text-fill-color:#00bbee;-webkit-text-stroke:1px#00bbee;', 'font-size:12px;');
|
||||
|
||||
function UsageBar(props) {
|
||||
let {usage} = props;
|
||||
usage = usage || 0;
|
||||
usage = Math.round(usage * 100) / 100;
|
||||
let {usage} = props;
|
||||
usage = usage || 0;
|
||||
usage = Math.round(usage * 100) / 100;
|
||||
|
||||
return (
|
||||
<Tooltip
|
||||
title={props.title??`${usage}%`}
|
||||
overlayInnerStyle={{
|
||||
whiteSpace: 'nowrap',
|
||||
wordBreak: 'keep-all',
|
||||
maxWidth: '300px',
|
||||
}}
|
||||
overlayStyle={{
|
||||
maxWidth: '300px',
|
||||
}}
|
||||
>
|
||||
<Progress percent={usage} showInfo={false} strokeWidth={12} trailColor='#FFECFF'/>
|
||||
</Tooltip>
|
||||
);
|
||||
return (
|
||||
<Tooltip
|
||||
title={props.title??`${usage}%`}
|
||||
overlayInnerStyle={{
|
||||
whiteSpace: 'nowrap',
|
||||
wordBreak: 'keep-all',
|
||||
maxWidth: '300px',
|
||||
}}
|
||||
overlayStyle={{
|
||||
maxWidth: '300px',
|
||||
}}
|
||||
>
|
||||
<Progress percent={usage} showInfo={false} strokeWidth={12} trailColor='#FFECFF'/>
|
||||
</Tooltip>
|
||||
);
|
||||
}
|
||||
|
||||
function overview(props) {
|
||||
const [runner, setRunner] = useState(false);
|
||||
const [desktop, setDesktop] = useState(false);
|
||||
const [procMgr, setProcMgr] = useState(false);
|
||||
const [explorer, setExplorer] = useState(false);
|
||||
const [generate, setGenerate] = useState(false);
|
||||
const [terminal, setTerminal] = useState(false);
|
||||
const [screenBlob, setScreenBlob] = useState('');
|
||||
const [isWindows, setIsWindows] = useState(false);
|
||||
const [dataSource, setDataSource] = useState([]);
|
||||
const [columnsState, setColumnsState] = useState(getInitColumnsState());
|
||||
const [runner, setRunner] = useState(false);
|
||||
const [desktop, setDesktop] = useState(false);
|
||||
const [procMgr, setProcMgr] = useState(false);
|
||||
const [explorer, setExplorer] = useState(false);
|
||||
const [generate, setGenerate] = useState(false);
|
||||
const [terminal, setTerminal] = useState(false);
|
||||
const [screenBlob, setScreenBlob] = useState('');
|
||||
const [isWindows, setIsWindows] = useState(false);
|
||||
const [dataSource, setDataSource] = useState([]);
|
||||
const [columnsState, setColumnsState] = useState(getInitColumnsState());
|
||||
|
||||
const columns = [
|
||||
{
|
||||
key: 'hostname',
|
||||
title: i18n.t('OVERVIEW.HOSTNAME'),
|
||||
dataIndex: 'hostname',
|
||||
ellipsis: true,
|
||||
width: 100
|
||||
},
|
||||
{
|
||||
key: 'username',
|
||||
title: i18n.t('OVERVIEW.USERNAME'),
|
||||
dataIndex: 'username',
|
||||
ellipsis: true,
|
||||
width: 90
|
||||
},
|
||||
{
|
||||
key: 'ping',
|
||||
title: 'Ping',
|
||||
dataIndex: 'latency',
|
||||
ellipsis: true,
|
||||
renderText: (v) => String(v) + 'ms',
|
||||
width: 60
|
||||
},
|
||||
{
|
||||
key: 'cpu_usage',
|
||||
title: i18n.t('OVERVIEW.CPU_USAGE'),
|
||||
dataIndex: 'cpu_usage',
|
||||
ellipsis: true,
|
||||
render: (_, v) => <UsageBar title={renderCPUStat(v.cpu)} {...v.cpu} />,
|
||||
width: 100
|
||||
},
|
||||
{
|
||||
key: 'ram_usage',
|
||||
title: i18n.t('OVERVIEW.RAM_USAGE'),
|
||||
dataIndex: 'ram_usage',
|
||||
ellipsis: true,
|
||||
render: (_, v) => <UsageBar title={renderRAMStat(v.ram)} {...v.ram} />,
|
||||
width: 100
|
||||
},
|
||||
{
|
||||
key: 'disk_usage',
|
||||
title: i18n.t('OVERVIEW.DISK_USAGE'),
|
||||
dataIndex: 'disk_usage',
|
||||
ellipsis: true,
|
||||
render: (_, v) => <UsageBar title={renderDiskStat(v.disk)} {...v.disk} />,
|
||||
width: 100
|
||||
},
|
||||
{
|
||||
key: 'os',
|
||||
title: i18n.t('OVERVIEW.OS'),
|
||||
dataIndex: 'os',
|
||||
ellipsis: true,
|
||||
width: 80
|
||||
},
|
||||
{
|
||||
key: 'arch',
|
||||
title: i18n.t('OVERVIEW.ARCH'),
|
||||
dataIndex: 'arch',
|
||||
ellipsis: true,
|
||||
width: 70
|
||||
},
|
||||
{
|
||||
key: 'ram_total',
|
||||
title: i18n.t('OVERVIEW.RAM'),
|
||||
dataIndex: 'ram_total',
|
||||
ellipsis: true,
|
||||
renderText: formatSize,
|
||||
width: 70
|
||||
},
|
||||
{
|
||||
key: 'mac',
|
||||
title: 'MAC',
|
||||
dataIndex: 'mac',
|
||||
ellipsis: true,
|
||||
width: 100
|
||||
},
|
||||
{
|
||||
key: 'lan',
|
||||
title: 'LAN',
|
||||
dataIndex: 'lan',
|
||||
ellipsis: true,
|
||||
width: 100
|
||||
},
|
||||
{
|
||||
key: 'wan',
|
||||
title: 'WAN',
|
||||
dataIndex: 'wan',
|
||||
ellipsis: true,
|
||||
width: 100
|
||||
},
|
||||
{
|
||||
key: 'uptime',
|
||||
title: i18n.t('OVERVIEW.UPTIME'),
|
||||
dataIndex: 'uptime',
|
||||
ellipsis: true,
|
||||
renderText: tsToTime,
|
||||
width: 100
|
||||
},
|
||||
{
|
||||
key: 'net_stat',
|
||||
title: i18n.t('OVERVIEW.NETWORK'),
|
||||
ellipsis: true,
|
||||
renderText: (_, v) => renderNetworkIO(v),
|
||||
width: 170
|
||||
},
|
||||
{
|
||||
key: 'option',
|
||||
title: i18n.t('OVERVIEW.OPERATIONS'),
|
||||
dataIndex: 'id',
|
||||
valueType: 'option',
|
||||
ellipsis: false,
|
||||
render: (_, device) => renderOperation(device),
|
||||
width: 170
|
||||
},
|
||||
];
|
||||
const options = {
|
||||
show: true,
|
||||
density: true,
|
||||
setting: true,
|
||||
};
|
||||
const tableRef = useRef();
|
||||
const columns = [
|
||||
{
|
||||
key: 'hostname',
|
||||
title: i18n.t('OVERVIEW.HOSTNAME'),
|
||||
dataIndex: 'hostname',
|
||||
ellipsis: true,
|
||||
width: 100
|
||||
},
|
||||
{
|
||||
key: 'username',
|
||||
title: i18n.t('OVERVIEW.USERNAME'),
|
||||
dataIndex: 'username',
|
||||
ellipsis: true,
|
||||
width: 90
|
||||
},
|
||||
{
|
||||
key: 'ping',
|
||||
title: 'Ping',
|
||||
dataIndex: 'latency',
|
||||
ellipsis: true,
|
||||
renderText: (v) => String(v) + 'ms',
|
||||
width: 60
|
||||
},
|
||||
{
|
||||
key: 'cpu_usage',
|
||||
title: i18n.t('OVERVIEW.CPU_USAGE'),
|
||||
dataIndex: 'cpu_usage',
|
||||
ellipsis: true,
|
||||
render: (_, v) => <UsageBar title={renderCPUStat(v.cpu)} {...v.cpu} />,
|
||||
width: 100
|
||||
},
|
||||
{
|
||||
key: 'ram_usage',
|
||||
title: i18n.t('OVERVIEW.RAM_USAGE'),
|
||||
dataIndex: 'ram_usage',
|
||||
ellipsis: true,
|
||||
render: (_, v) => <UsageBar title={renderRAMStat(v.ram)} {...v.ram} />,
|
||||
width: 100
|
||||
},
|
||||
{
|
||||
key: 'disk_usage',
|
||||
title: i18n.t('OVERVIEW.DISK_USAGE'),
|
||||
dataIndex: 'disk_usage',
|
||||
ellipsis: true,
|
||||
render: (_, v) => <UsageBar title={renderDiskStat(v.disk)} {...v.disk} />,
|
||||
width: 100
|
||||
},
|
||||
{
|
||||
key: 'os',
|
||||
title: i18n.t('OVERVIEW.OS'),
|
||||
dataIndex: 'os',
|
||||
ellipsis: true,
|
||||
width: 80
|
||||
},
|
||||
{
|
||||
key: 'arch',
|
||||
title: i18n.t('OVERVIEW.ARCH'),
|
||||
dataIndex: 'arch',
|
||||
ellipsis: true,
|
||||
width: 70
|
||||
},
|
||||
{
|
||||
key: 'ram_total',
|
||||
title: i18n.t('OVERVIEW.RAM'),
|
||||
dataIndex: 'ram_total',
|
||||
ellipsis: true,
|
||||
renderText: formatSize,
|
||||
width: 70
|
||||
},
|
||||
{
|
||||
key: 'mac',
|
||||
title: 'MAC',
|
||||
dataIndex: 'mac',
|
||||
ellipsis: true,
|
||||
width: 100
|
||||
},
|
||||
{
|
||||
key: 'lan',
|
||||
title: 'LAN',
|
||||
dataIndex: 'lan',
|
||||
ellipsis: true,
|
||||
width: 100
|
||||
},
|
||||
{
|
||||
key: 'wan',
|
||||
title: 'WAN',
|
||||
dataIndex: 'wan',
|
||||
ellipsis: true,
|
||||
width: 100
|
||||
},
|
||||
{
|
||||
key: 'uptime',
|
||||
title: i18n.t('OVERVIEW.UPTIME'),
|
||||
dataIndex: 'uptime',
|
||||
ellipsis: true,
|
||||
renderText: tsToTime,
|
||||
width: 100
|
||||
},
|
||||
{
|
||||
key: 'net_stat',
|
||||
title: i18n.t('OVERVIEW.NETWORK'),
|
||||
ellipsis: true,
|
||||
renderText: (_, v) => renderNetworkIO(v),
|
||||
width: 170
|
||||
},
|
||||
{
|
||||
key: 'option',
|
||||
title: i18n.t('OVERVIEW.OPERATIONS'),
|
||||
dataIndex: 'id',
|
||||
valueType: 'option',
|
||||
ellipsis: false,
|
||||
render: (_, device) => renderOperation(device),
|
||||
width: 170
|
||||
},
|
||||
];
|
||||
const options = {
|
||||
show: true,
|
||||
density: true,
|
||||
setting: true,
|
||||
};
|
||||
const tableRef = useRef();
|
||||
|
||||
useEffect(() => {
|
||||
// Auto update is only available when all modal are closed.
|
||||
if (!runner && !desktop && !procMgr && !explorer && !generate && !terminal) {
|
||||
let id = setInterval(getData, 3000);
|
||||
return () => {
|
||||
clearInterval(id);
|
||||
};
|
||||
}
|
||||
}, [runner, desktop, procMgr, explorer, generate, terminal]);
|
||||
useEffect(() => {
|
||||
// Auto update is only available when all modal are closed.
|
||||
if (!runner && !desktop && !procMgr && !explorer && !generate && !terminal) {
|
||||
let id = setInterval(getData, 3000);
|
||||
return () => {
|
||||
clearInterval(id);
|
||||
};
|
||||
}
|
||||
}, [runner, desktop, procMgr, explorer, generate, terminal]);
|
||||
|
||||
function getInitColumnsState() {
|
||||
let data = localStorage.getItem(`columnsState`);
|
||||
if (data !== null) {
|
||||
let stateMap = {};
|
||||
try {
|
||||
stateMap = JSON.parse(data);
|
||||
} catch (e) {
|
||||
stateMap = {};
|
||||
}
|
||||
return stateMap
|
||||
} else {
|
||||
localStorage.setItem(`columnsState`, JSON.stringify(defaultColumnsState));
|
||||
}
|
||||
return defaultColumnsState;
|
||||
}
|
||||
function saveColumnsState(stateMap) {
|
||||
setColumnsState(stateMap);
|
||||
localStorage.setItem(`columnsState`, JSON.stringify(stateMap));
|
||||
}
|
||||
function getInitColumnsState() {
|
||||
let data = localStorage.getItem(`columnsState`);
|
||||
if (data !== null) {
|
||||
let stateMap = {};
|
||||
try {
|
||||
stateMap = JSON.parse(data);
|
||||
} catch (e) {
|
||||
stateMap = {};
|
||||
}
|
||||
return stateMap
|
||||
} else {
|
||||
localStorage.setItem(`columnsState`, JSON.stringify({}));
|
||||
return {};
|
||||
}
|
||||
}
|
||||
function saveColumnsState(stateMap) {
|
||||
setColumnsState(stateMap);
|
||||
localStorage.setItem(`columnsState`, JSON.stringify(stateMap));
|
||||
}
|
||||
|
||||
function renderCPUStat(cpu) {
|
||||
let { model, usage, cores } = cpu;
|
||||
usage = Math.round(usage * 100) / 100;
|
||||
cores = {
|
||||
physical: Math.max(cores.physical, 1),
|
||||
logical: Math.max(cores.logical, 1),
|
||||
}
|
||||
return (
|
||||
<div>
|
||||
<div
|
||||
style={{
|
||||
fontSize: '10px',
|
||||
}}
|
||||
>
|
||||
{model}
|
||||
</div>
|
||||
{i18n.t('OVERVIEW.CPU_USAGE') + i18n.t('COMMON.COLON') + usage + '%'}
|
||||
<br />
|
||||
{i18n.t('OVERVIEW.CPU_LOGICAL_CORES') + i18n.t('COMMON.COLON') + cores.logical}
|
||||
<br />
|
||||
{i18n.t('OVERVIEW.CPU_PHYSICAL_CORES') + i18n.t('COMMON.COLON') + cores.physical}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
function renderRAMStat(info) {
|
||||
let { usage, total, used } = info;
|
||||
usage = Math.round(usage * 100) / 100;
|
||||
return (
|
||||
<div>
|
||||
{i18n.t('OVERVIEW.RAM_USAGE') + i18n.t('COMMON.COLON') + usage + '%'}
|
||||
<br />
|
||||
{i18n.t('OVERVIEW.FREE') + i18n.t('COMMON.COLON') + formatSize(total - used)}
|
||||
<br />
|
||||
{i18n.t('OVERVIEW.USED') + i18n.t('COMMON.COLON') + formatSize(used)}
|
||||
<br />
|
||||
{i18n.t('OVERVIEW.TOTAL') + i18n.t('COMMON.COLON') + formatSize(total)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
function renderDiskStat(info) {
|
||||
let { usage, total, used } = info;
|
||||
usage = Math.round(usage * 100) / 100;
|
||||
return (
|
||||
<div>
|
||||
{i18n.t('OVERVIEW.DISK_USAGE') + i18n.t('COMMON.COLON') + usage + '%'}
|
||||
<br />
|
||||
{i18n.t('OVERVIEW.FREE') + i18n.t('COMMON.COLON') + formatSize(total - used)}
|
||||
<br />
|
||||
{i18n.t('OVERVIEW.USED') + i18n.t('COMMON.COLON') + formatSize(used)}
|
||||
<br />
|
||||
{i18n.t('OVERVIEW.TOTAL') + i18n.t('COMMON.COLON') + formatSize(total)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
function renderNetworkIO(device) {
|
||||
// Make unit starts with Kbps.
|
||||
let sent = device.net_sent * 8 / 1024;
|
||||
let recv = device.net_recv * 8 / 1024;
|
||||
return `${format(sent)} ↑ / ${format(recv)} ↓`;
|
||||
function renderCPUStat(cpu) {
|
||||
let { model, usage, cores } = cpu;
|
||||
usage = Math.round(usage * 100) / 100;
|
||||
cores = {
|
||||
physical: Math.max(cores.physical, 1),
|
||||
logical: Math.max(cores.logical, 1),
|
||||
}
|
||||
return (
|
||||
<div>
|
||||
<div
|
||||
style={{
|
||||
fontSize: '10px',
|
||||
}}
|
||||
>
|
||||
{model}
|
||||
</div>
|
||||
{i18n.t('OVERVIEW.CPU_USAGE') + i18n.t('COMMON.COLON') + usage + '%'}
|
||||
<br />
|
||||
{i18n.t('OVERVIEW.CPU_LOGICAL_CORES') + i18n.t('COMMON.COLON') + cores.logical}
|
||||
<br />
|
||||
{i18n.t('OVERVIEW.CPU_PHYSICAL_CORES') + i18n.t('COMMON.COLON') + cores.physical}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
function renderRAMStat(info) {
|
||||
let { usage, total, used } = info;
|
||||
usage = Math.round(usage * 100) / 100;
|
||||
return (
|
||||
<div>
|
||||
{i18n.t('OVERVIEW.RAM_USAGE') + i18n.t('COMMON.COLON') + usage + '%'}
|
||||
<br />
|
||||
{i18n.t('OVERVIEW.FREE') + i18n.t('COMMON.COLON') + formatSize(total - used)}
|
||||
<br />
|
||||
{i18n.t('OVERVIEW.USED') + i18n.t('COMMON.COLON') + formatSize(used)}
|
||||
<br />
|
||||
{i18n.t('OVERVIEW.TOTAL') + i18n.t('COMMON.COLON') + formatSize(total)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
function renderDiskStat(info) {
|
||||
let { usage, total, used } = info;
|
||||
usage = Math.round(usage * 100) / 100;
|
||||
return (
|
||||
<div>
|
||||
{i18n.t('OVERVIEW.DISK_USAGE') + i18n.t('COMMON.COLON') + usage + '%'}
|
||||
<br />
|
||||
{i18n.t('OVERVIEW.FREE') + i18n.t('COMMON.COLON') + formatSize(total - used)}
|
||||
<br />
|
||||
{i18n.t('OVERVIEW.USED') + i18n.t('COMMON.COLON') + formatSize(used)}
|
||||
<br />
|
||||
{i18n.t('OVERVIEW.TOTAL') + i18n.t('COMMON.COLON') + formatSize(total)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
function renderNetworkIO(device) {
|
||||
// Make unit starts with Kbps.
|
||||
let sent = device.net_sent * 8 / 1024;
|
||||
let recv = device.net_recv * 8 / 1024;
|
||||
return `${format(sent)} ↑ / ${format(recv)} ↓`;
|
||||
|
||||
function format(size) {
|
||||
if (size <= 1) return '0 Kbps';
|
||||
// Units array is large enough.
|
||||
let k = 1024,
|
||||
i = Math.floor(Math.log(size) / Math.log(k)),
|
||||
units = ['Kbps', 'Mbps', 'Gbps', 'Tbps'];
|
||||
return (size / Math.pow(k, i)).toFixed(1) + ' ' + units[i];
|
||||
}
|
||||
}
|
||||
function renderOperation(device) {
|
||||
let menus = [
|
||||
{key: 'run', name: i18n.t('OVERVIEW.RUN')},
|
||||
{key: 'desktop', name: i18n.t('OVERVIEW.DESKTOP')},
|
||||
{key: 'screenshot', name: i18n.t('OVERVIEW.SCREENSHOT')},
|
||||
{key: 'lock', name: i18n.t('OVERVIEW.LOCK')},
|
||||
{key: 'logoff', name: i18n.t('OVERVIEW.LOGOFF')},
|
||||
{key: 'hibernate', name: i18n.t('OVERVIEW.HIBERNATE')},
|
||||
{key: 'suspend', name: i18n.t('OVERVIEW.SUSPEND')},
|
||||
{key: 'restart', name: i18n.t('OVERVIEW.RESTART')},
|
||||
{key: 'shutdown', name: i18n.t('OVERVIEW.SHUTDOWN')},
|
||||
{key: 'offline', name: i18n.t('OVERVIEW.OFFLINE')},
|
||||
];
|
||||
return [
|
||||
<a key='terminal' onClick={setTerminal.bind(null, device)}>{i18n.t('OVERVIEW.TERMINAL')}</a>,
|
||||
<a key='procmgr' onClick={setProcMgr.bind(null, device.id)}>{i18n.t('OVERVIEW.PROC_MANAGER')}</a>,
|
||||
<a key='explorer' onClick={() => {
|
||||
setExplorer(device.id);
|
||||
setIsWindows(device.os === 'windows');
|
||||
}}>
|
||||
{i18n.t('OVERVIEW.EXPLORER')}
|
||||
</a>,
|
||||
<TableDropdown
|
||||
key='more'
|
||||
onSelect={key => callDevice(key, device)}
|
||||
menus={menus}
|
||||
/>,
|
||||
]
|
||||
}
|
||||
function format(size) {
|
||||
if (size <= 1) return '0 Kbps';
|
||||
// Units array is large enough.
|
||||
let k = 1024,
|
||||
i = Math.floor(Math.log(size) / Math.log(k)),
|
||||
units = ['Kbps', 'Mbps', 'Gbps', 'Tbps'];
|
||||
return (size / Math.pow(k, i)).toFixed(1) + ' ' + units[i];
|
||||
}
|
||||
}
|
||||
function renderOperation(device) {
|
||||
let menus = [
|
||||
{key: 'run', name: i18n.t('OVERVIEW.RUN')},
|
||||
{key: 'desktop', name: i18n.t('OVERVIEW.DESKTOP')},
|
||||
{key: 'screenshot', name: i18n.t('OVERVIEW.SCREENSHOT')},
|
||||
{key: 'lock', name: i18n.t('OVERVIEW.LOCK')},
|
||||
{key: 'logoff', name: i18n.t('OVERVIEW.LOGOFF')},
|
||||
{key: 'hibernate', name: i18n.t('OVERVIEW.HIBERNATE')},
|
||||
{key: 'suspend', name: i18n.t('OVERVIEW.SUSPEND')},
|
||||
{key: 'restart', name: i18n.t('OVERVIEW.RESTART')},
|
||||
{key: 'shutdown', name: i18n.t('OVERVIEW.SHUTDOWN')},
|
||||
{key: 'offline', name: i18n.t('OVERVIEW.OFFLINE')},
|
||||
];
|
||||
return [
|
||||
<a key='terminal' onClick={setTerminal.bind(null, device)}>{i18n.t('OVERVIEW.TERMINAL')}</a>,
|
||||
<a key='procmgr' onClick={setProcMgr.bind(null, device.id)}>{i18n.t('OVERVIEW.PROC_MANAGER')}</a>,
|
||||
<a key='explorer' onClick={() => {
|
||||
setExplorer(device.id);
|
||||
setIsWindows(device.os === 'windows');
|
||||
}}>
|
||||
{i18n.t('OVERVIEW.EXPLORER')}
|
||||
</a>,
|
||||
<TableDropdown
|
||||
key='more'
|
||||
onSelect={key => callDevice(key, device)}
|
||||
menus={menus}
|
||||
/>,
|
||||
]
|
||||
}
|
||||
|
||||
function callDevice(act, device) {
|
||||
if (act === 'run') {
|
||||
setRunner(device);
|
||||
return;
|
||||
}
|
||||
if (act === 'desktop') {
|
||||
setDesktop(device);
|
||||
return;
|
||||
}
|
||||
if (act === 'screenshot') {
|
||||
request('/api/device/screenshot/get', {device: device.id}, {}, {
|
||||
responseType: 'blob'
|
||||
}).then(res => {
|
||||
if ((res.data.type ?? '').substring(0, 5) === 'image') {
|
||||
if (screenBlob.length > 0) {
|
||||
URL.revokeObjectURL(screenBlob);
|
||||
}
|
||||
setScreenBlob(URL.createObjectURL(res.data));
|
||||
}
|
||||
}).catch(catchBlobReq);
|
||||
return;
|
||||
}
|
||||
Modal.confirm({
|
||||
title: i18n.t('OVERVIEW.OPERATION_CONFIRM').replace('{0}', i18n.t('OVERVIEW.'+act.toUpperCase())),
|
||||
icon: <QuestionCircleOutlined/>,
|
||||
onOk() {
|
||||
request('/api/device/' + act, {device: device.id}).then(res => {
|
||||
let data = res.data;
|
||||
if (data.code === 0) {
|
||||
message.success(i18n.t('OVERVIEW.OPERATION_SUCCESS'));
|
||||
tableRef.current.reload();
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
function callDevice(act, device) {
|
||||
if (act === 'run') {
|
||||
setRunner(device);
|
||||
return;
|
||||
}
|
||||
if (act === 'desktop') {
|
||||
setDesktop(device);
|
||||
return;
|
||||
}
|
||||
if (act === 'screenshot') {
|
||||
request('/api/device/screenshot/get', {device: device.id}, {}, {
|
||||
responseType: 'blob'
|
||||
}).then(res => {
|
||||
if ((res.data.type ?? '').substring(0, 5) === 'image') {
|
||||
if (screenBlob.length > 0) {
|
||||
URL.revokeObjectURL(screenBlob);
|
||||
}
|
||||
setScreenBlob(URL.createObjectURL(res.data));
|
||||
}
|
||||
}).catch(catchBlobReq);
|
||||
return;
|
||||
}
|
||||
Modal.confirm({
|
||||
title: i18n.t('OVERVIEW.OPERATION_CONFIRM').replace('{0}', i18n.t('OVERVIEW.'+act.toUpperCase())),
|
||||
icon: <QuestionCircleOutlined/>,
|
||||
onOk() {
|
||||
request('/api/device/' + act, {device: device.id}).then(res => {
|
||||
let data = res.data;
|
||||
if (data.code === 0) {
|
||||
message.success(i18n.t('OVERVIEW.OPERATION_SUCCESS'));
|
||||
tableRef.current.reload();
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function toolBar() {
|
||||
return (
|
||||
<Button type='primary' onClick={setGenerate.bind(null, true)}>{i18n.t('OVERVIEW.GENERATE')}</Button>
|
||||
)
|
||||
}
|
||||
function toolBar() {
|
||||
return (
|
||||
<Button type='primary' onClick={setGenerate.bind(null, true)}>{i18n.t('OVERVIEW.GENERATE')}</Button>
|
||||
)
|
||||
}
|
||||
|
||||
async function getData(form) {
|
||||
await waitTime(300);
|
||||
let res = await request('/api/device/list');
|
||||
let data = res.data;
|
||||
if (data.code === 0) {
|
||||
let result = [];
|
||||
for (const uuid in data.data) {
|
||||
let temp = data.data[uuid];
|
||||
temp.conn = uuid;
|
||||
result.push(temp);
|
||||
}
|
||||
// Iterate all object and expand them.
|
||||
for (let i = 0; i < result.length; i++) {
|
||||
for (const k in result[i]) {
|
||||
if (typeof result[i][k] === 'object') {
|
||||
for (const key in result[i][k]) {
|
||||
result[i][k + '_' + key] = result[i][k][key];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
result = result.sort((first, second) => {
|
||||
let firstEl = first.hostname.toUpperCase();
|
||||
let secondEl = second.hostname.toUpperCase();
|
||||
if (firstEl < secondEl) return -1;
|
||||
if (firstEl > secondEl) return 1;
|
||||
return 0;
|
||||
});
|
||||
result = result.sort((first, second) => {
|
||||
let firstEl = first.os.toUpperCase();
|
||||
let secondEl = second.os.toUpperCase();
|
||||
if (firstEl < secondEl) return -1;
|
||||
if (firstEl > secondEl) return 1;
|
||||
return 0;
|
||||
});
|
||||
setDataSource(result);
|
||||
return ({
|
||||
data: result,
|
||||
success: true,
|
||||
total: result.length
|
||||
});
|
||||
}
|
||||
return ({data: [], success: false, total: 0});
|
||||
}
|
||||
async function getData(form) {
|
||||
await waitTime(300);
|
||||
let res = await request('/api/device/list');
|
||||
let data = res.data;
|
||||
if (data.code === 0) {
|
||||
let result = [];
|
||||
for (const uuid in data.data) {
|
||||
let temp = data.data[uuid];
|
||||
temp.conn = uuid;
|
||||
result.push(temp);
|
||||
}
|
||||
// Iterate all object and expand them.
|
||||
for (let i = 0; i < result.length; i++) {
|
||||
for (const k in result[i]) {
|
||||
if (typeof result[i][k] === 'object') {
|
||||
for (const key in result[i][k]) {
|
||||
result[i][k + '_' + key] = result[i][k][key];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
result = result.sort((first, second) => {
|
||||
let firstEl = first.hostname.toUpperCase();
|
||||
let secondEl = second.hostname.toUpperCase();
|
||||
if (firstEl < secondEl) return -1;
|
||||
if (firstEl > secondEl) return 1;
|
||||
return 0;
|
||||
});
|
||||
result = result.sort((first, second) => {
|
||||
let firstEl = first.os.toUpperCase();
|
||||
let secondEl = second.os.toUpperCase();
|
||||
if (firstEl < secondEl) return -1;
|
||||
if (firstEl > secondEl) return 1;
|
||||
return 0;
|
||||
});
|
||||
setDataSource(result);
|
||||
return ({
|
||||
data: result,
|
||||
success: true,
|
||||
total: result.length
|
||||
});
|
||||
}
|
||||
return ({data: [], success: false, total: 0});
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
<Image
|
||||
preview={{
|
||||
visible: screenBlob,
|
||||
src: screenBlob,
|
||||
onVisibleChange: () => {
|
||||
URL.revokeObjectURL(screenBlob);
|
||||
setScreenBlob('');
|
||||
}
|
||||
}}
|
||||
/>
|
||||
<Generate
|
||||
visible={generate}
|
||||
onVisibleChange={setGenerate}
|
||||
/>
|
||||
<Explorer
|
||||
isWindows={isWindows}
|
||||
visible={explorer}
|
||||
device={explorer}
|
||||
onCancel={setExplorer.bind(null, false)}
|
||||
/>
|
||||
<ProcMgr
|
||||
visible={procMgr}
|
||||
device={procMgr}
|
||||
onCancel={setProcMgr.bind(null, false)}
|
||||
/>
|
||||
<Runner
|
||||
visible={runner}
|
||||
device={runner}
|
||||
onCancel={setRunner.bind(null, false)}
|
||||
/>
|
||||
<Desktop
|
||||
visible={desktop}
|
||||
device={desktop}
|
||||
onCancel={setDesktop.bind(null, false)}
|
||||
/>
|
||||
<Terminal
|
||||
visible={terminal}
|
||||
device={terminal}
|
||||
onCancel={setTerminal.bind(null, false)}
|
||||
/>
|
||||
<ProTable
|
||||
scroll={{
|
||||
x: 'max-content',
|
||||
scrollToFirstRowOnChange: true
|
||||
}}
|
||||
rowKey='id'
|
||||
search={false}
|
||||
options={options}
|
||||
columns={columns}
|
||||
columnsState={{
|
||||
value: columnsState,
|
||||
onChange: saveColumnsState
|
||||
}}
|
||||
request={getData}
|
||||
pagination={false}
|
||||
actionRef={tableRef}
|
||||
toolBarRender={toolBar}
|
||||
dataSource={dataSource}
|
||||
onDataSourceChange={setDataSource}
|
||||
/>
|
||||
</>
|
||||
);
|
||||
return (
|
||||
<>
|
||||
<Image
|
||||
preview={{
|
||||
visible: screenBlob,
|
||||
src: screenBlob,
|
||||
onVisibleChange: () => {
|
||||
URL.revokeObjectURL(screenBlob);
|
||||
setScreenBlob('');
|
||||
}
|
||||
}}
|
||||
/>
|
||||
<Generate
|
||||
visible={generate}
|
||||
onVisibleChange={setGenerate}
|
||||
/>
|
||||
<Explorer
|
||||
isWindows={isWindows}
|
||||
visible={explorer}
|
||||
device={explorer}
|
||||
onCancel={setExplorer.bind(null, false)}
|
||||
/>
|
||||
<ProcMgr
|
||||
visible={procMgr}
|
||||
device={procMgr}
|
||||
onCancel={setProcMgr.bind(null, false)}
|
||||
/>
|
||||
<Runner
|
||||
visible={runner}
|
||||
device={runner}
|
||||
onCancel={setRunner.bind(null, false)}
|
||||
/>
|
||||
<Desktop
|
||||
visible={desktop}
|
||||
device={desktop}
|
||||
onCancel={setDesktop.bind(null, false)}
|
||||
/>
|
||||
<Terminal
|
||||
visible={terminal}
|
||||
device={terminal}
|
||||
onCancel={setTerminal.bind(null, false)}
|
||||
/>
|
||||
<ProTable
|
||||
scroll={{
|
||||
x: 'max-content',
|
||||
scrollToFirstRowOnChange: true
|
||||
}}
|
||||
rowKey='id'
|
||||
search={false}
|
||||
options={options}
|
||||
columns={columns}
|
||||
columnsState={{
|
||||
value: columnsState,
|
||||
onChange: saveColumnsState
|
||||
}}
|
||||
request={getData}
|
||||
pagination={false}
|
||||
actionRef={tableRef}
|
||||
toolBarRender={toolBar}
|
||||
dataSource={dataSource}
|
||||
onDataSourceChange={setDataSource}
|
||||
/>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
function wrapper(props) {
|
||||
let Component = overview;
|
||||
return (<Component {...props} key={Math.random()}/>)
|
||||
let Component = overview;
|
||||
return (<Component {...props} key={Math.random()}/>)
|
||||
}
|
||||
|
||||
export default wrapper;
|
@@ -1,99 +1,99 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<svg width="1361px" height="609px" viewBox="0 0 1361 609" version="1.1" xmlns="http://www.w3.org/2000/svg">
|
||||
<g stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
|
||||
<g transform="translate(-79.000000, -82.000000)">
|
||||
<g transform="translate(77.000000, 73.000000)">
|
||||
<g opacity="0.8"
|
||||
transform="translate(74.901416, 569.699158) rotate(-7.000000) translate(-74.901416, -569.699158) translate(4.901416, 525.199158)">
|
||||
<ellipse fill="#CFDAE6" opacity="0.25" cx="63.5748792" cy="32.468367" rx="21.7830479"
|
||||
ry="21.766008"></ellipse>
|
||||
<ellipse fill="#CFDAE6" opacity="0.599999964" cx="5.98746479" cy="13.8668601" rx="5.2173913"
|
||||
ry="5.21330997"></ellipse>
|
||||
<path d="M38.1354514,88.3520215 C43.8984227,88.3520215 48.570234,83.6838647 48.570234,77.9254015 C48.570234,72.1669383 43.8984227,67.4987816 38.1354514,67.4987816 C32.3724801,67.4987816 27.7006688,72.1669383 27.7006688,77.9254015 C27.7006688,83.6838647 32.3724801,88.3520215 38.1354514,88.3520215 Z"
|
||||
fill="#CFDAE6" opacity="0.45"></path>
|
||||
<path d="M64.2775582,33.1704963 L119.185836,16.5654915" stroke="#CFDAE6" stroke-width="1.73913043"
|
||||
stroke-linecap="round" stroke-linejoin="round"></path>
|
||||
<path d="M42.1431708,26.5002681 L7.71190162,14.5640702" stroke="#E0B4B7" stroke-width="0.702678964"
|
||||
opacity="0.7" stroke-linecap="round" stroke-linejoin="round"
|
||||
stroke-dasharray="1.405357899873153,2.108036953469981"></path>
|
||||
<path d="M63.9262187,33.521561 L43.6721326,69.3250951" stroke="#BACAD9" stroke-width="0.702678964"
|
||||
stroke-linecap="round" stroke-linejoin="round"
|
||||
stroke-dasharray="1.405357899873153,2.108036953469981"></path>
|
||||
<g transform="translate(126.850922, 13.543654) rotate(30.000000) translate(-126.850922, -13.543654) translate(117.285705, 4.381889)"
|
||||
fill="#CFDAE6">
|
||||
<ellipse opacity="0.45" cx="9.13482653" cy="9.12768076" rx="9.13482653"
|
||||
ry="9.12768076"></ellipse>
|
||||
<path d="M18.2696531,18.2553615 C18.2696531,13.2142826 14.1798519,9.12768076 9.13482653,9.12768076 C4.08980114,9.12768076 0,13.2142826 0,18.2553615 L18.2696531,18.2553615 Z"
|
||||
transform="translate(9.134827, 13.691521) scale(-1, -1) translate(-9.134827, -13.691521) "></path>
|
||||
</g>
|
||||
</g>
|
||||
<g transform="translate(216.294700, 123.725600) rotate(-5.000000) translate(-216.294700, -123.725600) translate(106.294700, 35.225600)">
|
||||
<ellipse fill="#CFDAE6" opacity="0.25" cx="29.1176471" cy="29.1402439" rx="29.1176471"
|
||||
ry="29.1402439"></ellipse>
|
||||
<ellipse fill="#CFDAE6" opacity="0.3" cx="29.1176471" cy="29.1402439" rx="21.5686275"
|
||||
ry="21.5853659"></ellipse>
|
||||
<ellipse stroke="#CFDAE6" opacity="0.4" cx="179.019608" cy="138.146341" rx="23.7254902"
|
||||
ry="23.7439024"></ellipse>
|
||||
<ellipse fill="#BACAD9" opacity="0.5" cx="29.1176471" cy="29.1402439" rx="10.7843137"
|
||||
ry="10.7926829"></ellipse>
|
||||
<path d="M29.1176471,39.9329268 L29.1176471,18.347561 C23.1616351,18.347561 18.3333333,23.1796097 18.3333333,29.1402439 C18.3333333,35.1008781 23.1616351,39.9329268 29.1176471,39.9329268 Z"
|
||||
fill="#BACAD9"></path>
|
||||
<g opacity="0.45" transform="translate(172.000000, 131.000000)" fill="#E6A1A6">
|
||||
<ellipse cx="7.01960784" cy="7.14634146" rx="6.47058824" ry="6.47560976"></ellipse>
|
||||
<path d="M0.549019608,13.6219512 C4.12262681,13.6219512 7.01960784,10.722722 7.01960784,7.14634146 C7.01960784,3.56996095 4.12262681,0.670731707 0.549019608,0.670731707 L0.549019608,13.6219512 Z"
|
||||
transform="translate(3.784314, 7.146341) scale(-1, 1) translate(-3.784314, -7.146341) "></path>
|
||||
</g>
|
||||
<ellipse fill="#CFDAE6" cx="218.382353" cy="138.685976" rx="1.61764706" ry="1.61890244"></ellipse>
|
||||
<ellipse fill="#E0B4B7" opacity="0.35" cx="179.558824" cy="175.381098" rx="1.61764706"
|
||||
ry="1.61890244"></ellipse>
|
||||
<ellipse fill="#E0B4B7" opacity="0.35" cx="180.098039" cy="102.530488" rx="2.15686275"
|
||||
ry="2.15853659"></ellipse>
|
||||
<path d="M28.9985381,29.9671598 L171.151018,132.876024" stroke="#CFDAE6" opacity="0.8"></path>
|
||||
</g>
|
||||
<g opacity="0.799999952"
|
||||
transform="translate(1054.100635, 36.659317) rotate(-11.000000) translate(-1054.100635, -36.659317) translate(1026.600635, 4.659317)">
|
||||
<ellipse stroke="#CFDAE6" stroke-width="0.941176471" cx="43.8135593" cy="32" rx="11.1864407"
|
||||
ry="11.2941176"></ellipse>
|
||||
<g transform="translate(34.596774, 23.111111)" fill="#BACAD9">
|
||||
<ellipse opacity="0.45" cx="9.18534718" cy="8.88888889" rx="8.47457627"
|
||||
ry="8.55614973"></ellipse>
|
||||
<path d="M9.18534718,17.4450386 C13.8657264,17.4450386 17.6599235,13.6143199 17.6599235,8.88888889 C17.6599235,4.16345787 13.8657264,0.332739156 9.18534718,0.332739156 L9.18534718,17.4450386 Z"></path>
|
||||
</g>
|
||||
<path d="M34.6597385,24.809694 L5.71666084,4.76878945" stroke="#CFDAE6"
|
||||
stroke-width="0.941176471"></path>
|
||||
<ellipse stroke="#CFDAE6" stroke-width="0.941176471" cx="3.26271186" cy="3.29411765" rx="3.26271186"
|
||||
ry="3.29411765"></ellipse>
|
||||
<ellipse fill="#F7E1AD" cx="2.79661017" cy="61.1764706" rx="2.79661017" ry="2.82352941"></ellipse>
|
||||
<path d="M34.6312443,39.2922712 L5.06366663,59.785082" stroke="#CFDAE6"
|
||||
stroke-width="0.941176471"></path>
|
||||
</g>
|
||||
<g opacity="0.33"
|
||||
transform="translate(1282.537219, 446.502867) rotate(-10.000000) translate(-1282.537219, -446.502867) translate(1142.537219, 327.502867)">
|
||||
<g transform="translate(141.333539, 104.502742) rotate(275.000000) translate(-141.333539, -104.502742) translate(129.333539, 92.502742)"
|
||||
fill="#BACAD9">
|
||||
<circle opacity="0.45" cx="11.6666667" cy="11.6666667" r="11.6666667"></circle>
|
||||
<path d="M23.3333333,23.3333333 C23.3333333,16.8900113 18.1099887,11.6666667 11.6666667,11.6666667 C5.22334459,11.6666667 0,16.8900113 0,23.3333333 L23.3333333,23.3333333 Z"
|
||||
transform="translate(11.666667, 17.500000) scale(-1, -1) translate(-11.666667, -17.500000) "></path>
|
||||
</g>
|
||||
<circle fill="#CFDAE6" cx="201.833333" cy="87.5" r="5.83333333"></circle>
|
||||
<path d="M143.5,88.8126685 L155.070501,17.6038544" stroke="#BACAD9"
|
||||
stroke-width="1.16666667"></path>
|
||||
<path d="M17.5,37.3333333 L127.466252,97.6449735" stroke="#BACAD9" stroke-width="1.16666667"></path>
|
||||
<polyline stroke="#CFDAE6" stroke-width="1.16666667"
|
||||
points="143.902597 120.302281 174.935455 231.571342 38.5 147.510847 126.366941 110.833333"></polyline>
|
||||
<path d="M159.833333,99.7453842 L195.416667,89.25" stroke="#E0B4B7" stroke-width="1.16666667"
|
||||
opacity="0.6"></path>
|
||||
<path d="M205.333333,82.1372105 L238.719406,36.1666667" stroke="#BACAD9"
|
||||
stroke-width="1.16666667"></path>
|
||||
<path d="M266.723424,132.231988 L207.083333,90.4166667" stroke="#CFDAE6"
|
||||
stroke-width="1.16666667"></path>
|
||||
<circle fill="#C1D1E0" cx="156.916667" cy="8.75" r="8.75"></circle>
|
||||
<circle fill="#C1D1E0" cx="39.0833333" cy="148.75" r="5.25"></circle>
|
||||
<circle fill-opacity="0.6" fill="#D1DEED" cx="8.75" cy="33.25" r="8.75"></circle>
|
||||
<circle fill-opacity="0.6" fill="#D1DEED" cx="243.833333" cy="30.3333333" r="5.83333333"></circle>
|
||||
<circle fill="#E0B4B7" cx="175.583333" cy="232.75" r="5.25"></circle>
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
<g stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
|
||||
<g transform="translate(-79.000000, -82.000000)">
|
||||
<g transform="translate(77.000000, 73.000000)">
|
||||
<g opacity="0.8"
|
||||
transform="translate(74.901416, 569.699158) rotate(-7.000000) translate(-74.901416, -569.699158) translate(4.901416, 525.199158)">
|
||||
<ellipse fill="#CFDAE6" opacity="0.25" cx="63.5748792" cy="32.468367" rx="21.7830479"
|
||||
ry="21.766008"></ellipse>
|
||||
<ellipse fill="#CFDAE6" opacity="0.599999964" cx="5.98746479" cy="13.8668601" rx="5.2173913"
|
||||
ry="5.21330997"></ellipse>
|
||||
<path d="M38.1354514,88.3520215 C43.8984227,88.3520215 48.570234,83.6838647 48.570234,77.9254015 C48.570234,72.1669383 43.8984227,67.4987816 38.1354514,67.4987816 C32.3724801,67.4987816 27.7006688,72.1669383 27.7006688,77.9254015 C27.7006688,83.6838647 32.3724801,88.3520215 38.1354514,88.3520215 Z"
|
||||
fill="#CFDAE6" opacity="0.45"></path>
|
||||
<path d="M64.2775582,33.1704963 L119.185836,16.5654915" stroke="#CFDAE6" stroke-width="1.73913043"
|
||||
stroke-linecap="round" stroke-linejoin="round"></path>
|
||||
<path d="M42.1431708,26.5002681 L7.71190162,14.5640702" stroke="#E0B4B7" stroke-width="0.702678964"
|
||||
opacity="0.7" stroke-linecap="round" stroke-linejoin="round"
|
||||
stroke-dasharray="1.405357899873153,2.108036953469981"></path>
|
||||
<path d="M63.9262187,33.521561 L43.6721326,69.3250951" stroke="#BACAD9" stroke-width="0.702678964"
|
||||
stroke-linecap="round" stroke-linejoin="round"
|
||||
stroke-dasharray="1.405357899873153,2.108036953469981"></path>
|
||||
<g transform="translate(126.850922, 13.543654) rotate(30.000000) translate(-126.850922, -13.543654) translate(117.285705, 4.381889)"
|
||||
fill="#CFDAE6">
|
||||
<ellipse opacity="0.45" cx="9.13482653" cy="9.12768076" rx="9.13482653"
|
||||
ry="9.12768076"></ellipse>
|
||||
<path d="M18.2696531,18.2553615 C18.2696531,13.2142826 14.1798519,9.12768076 9.13482653,9.12768076 C4.08980114,9.12768076 0,13.2142826 0,18.2553615 L18.2696531,18.2553615 Z"
|
||||
transform="translate(9.134827, 13.691521) scale(-1, -1) translate(-9.134827, -13.691521) "></path>
|
||||
</g>
|
||||
</g>
|
||||
<g transform="translate(216.294700, 123.725600) rotate(-5.000000) translate(-216.294700, -123.725600) translate(106.294700, 35.225600)">
|
||||
<ellipse fill="#CFDAE6" opacity="0.25" cx="29.1176471" cy="29.1402439" rx="29.1176471"
|
||||
ry="29.1402439"></ellipse>
|
||||
<ellipse fill="#CFDAE6" opacity="0.3" cx="29.1176471" cy="29.1402439" rx="21.5686275"
|
||||
ry="21.5853659"></ellipse>
|
||||
<ellipse stroke="#CFDAE6" opacity="0.4" cx="179.019608" cy="138.146341" rx="23.7254902"
|
||||
ry="23.7439024"></ellipse>
|
||||
<ellipse fill="#BACAD9" opacity="0.5" cx="29.1176471" cy="29.1402439" rx="10.7843137"
|
||||
ry="10.7926829"></ellipse>
|
||||
<path d="M29.1176471,39.9329268 L29.1176471,18.347561 C23.1616351,18.347561 18.3333333,23.1796097 18.3333333,29.1402439 C18.3333333,35.1008781 23.1616351,39.9329268 29.1176471,39.9329268 Z"
|
||||
fill="#BACAD9"></path>
|
||||
<g opacity="0.45" transform="translate(172.000000, 131.000000)" fill="#E6A1A6">
|
||||
<ellipse cx="7.01960784" cy="7.14634146" rx="6.47058824" ry="6.47560976"></ellipse>
|
||||
<path d="M0.549019608,13.6219512 C4.12262681,13.6219512 7.01960784,10.722722 7.01960784,7.14634146 C7.01960784,3.56996095 4.12262681,0.670731707 0.549019608,0.670731707 L0.549019608,13.6219512 Z"
|
||||
transform="translate(3.784314, 7.146341) scale(-1, 1) translate(-3.784314, -7.146341) "></path>
|
||||
</g>
|
||||
<ellipse fill="#CFDAE6" cx="218.382353" cy="138.685976" rx="1.61764706" ry="1.61890244"></ellipse>
|
||||
<ellipse fill="#E0B4B7" opacity="0.35" cx="179.558824" cy="175.381098" rx="1.61764706"
|
||||
ry="1.61890244"></ellipse>
|
||||
<ellipse fill="#E0B4B7" opacity="0.35" cx="180.098039" cy="102.530488" rx="2.15686275"
|
||||
ry="2.15853659"></ellipse>
|
||||
<path d="M28.9985381,29.9671598 L171.151018,132.876024" stroke="#CFDAE6" opacity="0.8"></path>
|
||||
</g>
|
||||
<g opacity="0.799999952"
|
||||
transform="translate(1054.100635, 36.659317) rotate(-11.000000) translate(-1054.100635, -36.659317) translate(1026.600635, 4.659317)">
|
||||
<ellipse stroke="#CFDAE6" stroke-width="0.941176471" cx="43.8135593" cy="32" rx="11.1864407"
|
||||
ry="11.2941176"></ellipse>
|
||||
<g transform="translate(34.596774, 23.111111)" fill="#BACAD9">
|
||||
<ellipse opacity="0.45" cx="9.18534718" cy="8.88888889" rx="8.47457627"
|
||||
ry="8.55614973"></ellipse>
|
||||
<path d="M9.18534718,17.4450386 C13.8657264,17.4450386 17.6599235,13.6143199 17.6599235,8.88888889 C17.6599235,4.16345787 13.8657264,0.332739156 9.18534718,0.332739156 L9.18534718,17.4450386 Z"></path>
|
||||
</g>
|
||||
<path d="M34.6597385,24.809694 L5.71666084,4.76878945" stroke="#CFDAE6"
|
||||
stroke-width="0.941176471"></path>
|
||||
<ellipse stroke="#CFDAE6" stroke-width="0.941176471" cx="3.26271186" cy="3.29411765" rx="3.26271186"
|
||||
ry="3.29411765"></ellipse>
|
||||
<ellipse fill="#F7E1AD" cx="2.79661017" cy="61.1764706" rx="2.79661017" ry="2.82352941"></ellipse>
|
||||
<path d="M34.6312443,39.2922712 L5.06366663,59.785082" stroke="#CFDAE6"
|
||||
stroke-width="0.941176471"></path>
|
||||
</g>
|
||||
<g opacity="0.33"
|
||||
transform="translate(1282.537219, 446.502867) rotate(-10.000000) translate(-1282.537219, -446.502867) translate(1142.537219, 327.502867)">
|
||||
<g transform="translate(141.333539, 104.502742) rotate(275.000000) translate(-141.333539, -104.502742) translate(129.333539, 92.502742)"
|
||||
fill="#BACAD9">
|
||||
<circle opacity="0.45" cx="11.6666667" cy="11.6666667" r="11.6666667"></circle>
|
||||
<path d="M23.3333333,23.3333333 C23.3333333,16.8900113 18.1099887,11.6666667 11.6666667,11.6666667 C5.22334459,11.6666667 0,16.8900113 0,23.3333333 L23.3333333,23.3333333 Z"
|
||||
transform="translate(11.666667, 17.500000) scale(-1, -1) translate(-11.666667, -17.500000) "></path>
|
||||
</g>
|
||||
<circle fill="#CFDAE6" cx="201.833333" cy="87.5" r="5.83333333"></circle>
|
||||
<path d="M143.5,88.8126685 L155.070501,17.6038544" stroke="#BACAD9"
|
||||
stroke-width="1.16666667"></path>
|
||||
<path d="M17.5,37.3333333 L127.466252,97.6449735" stroke="#BACAD9" stroke-width="1.16666667"></path>
|
||||
<polyline stroke="#CFDAE6" stroke-width="1.16666667"
|
||||
points="143.902597 120.302281 174.935455 231.571342 38.5 147.510847 126.366941 110.833333"></polyline>
|
||||
<path d="M159.833333,99.7453842 L195.416667,89.25" stroke="#E0B4B7" stroke-width="1.16666667"
|
||||
opacity="0.6"></path>
|
||||
<path d="M205.333333,82.1372105 L238.719406,36.1666667" stroke="#BACAD9"
|
||||
stroke-width="1.16666667"></path>
|
||||
<path d="M266.723424,132.231988 L207.083333,90.4166667" stroke="#CFDAE6"
|
||||
stroke-width="1.16666667"></path>
|
||||
<circle fill="#C1D1E0" cx="156.916667" cy="8.75" r="8.75"></circle>
|
||||
<circle fill="#C1D1E0" cx="39.0833333" cy="148.75" r="5.25"></circle>
|
||||
<circle fill-opacity="0.6" fill="#D1DEED" cx="8.75" cy="33.25" r="8.75"></circle>
|
||||
<circle fill-opacity="0.6" fill="#D1DEED" cx="243.833333" cy="30.3333333" r="5.83333333"></circle>
|
||||
<circle fill="#E0B4B7" cx="175.583333" cy="232.75" r="5.25"></circle>
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
Before Width: | Height: | Size: 8.7 KiB After Width: | Height: | Size: 7.2 KiB |
@@ -6,210 +6,210 @@ import CryptoJS from "crypto-js";
|
||||
|
||||
let orderCompare;
|
||||
try {
|
||||
let collator = new Intl.Collator(getLang(), {numeric: true, sensitivity: 'base'});
|
||||
orderCompare = collator.compare.bind(collator);
|
||||
let collator = new Intl.Collator(getLang(), {numeric: true, sensitivity: 'base'});
|
||||
orderCompare = collator.compare.bind(collator);
|
||||
} catch (e) {
|
||||
orderCompare = (a, b) => a - b;
|
||||
orderCompare = (a, b) => a - b;
|
||||
}
|
||||
|
||||
function request(url, data, headers, ext, noTrans) {
|
||||
let _headers = headers ?? {};
|
||||
_headers = Object.assign({'Content-Type': 'application/x-www-form-urlencoded'}, _headers);
|
||||
return axios(Object.assign({
|
||||
url: url,
|
||||
data: data,
|
||||
method: 'post',
|
||||
headers: _headers,
|
||||
transformRequest: noTrans ? [] : [Qs.stringify],
|
||||
}, ext??{}));
|
||||
let _headers = headers ?? {};
|
||||
_headers = Object.assign({'Content-Type': 'application/x-www-form-urlencoded'}, _headers);
|
||||
return axios(Object.assign({
|
||||
url: url,
|
||||
data: data,
|
||||
method: 'post',
|
||||
headers: _headers,
|
||||
transformRequest: noTrans ? [] : [Qs.stringify],
|
||||
}, ext??{}));
|
||||
}
|
||||
|
||||
function waitTime(time) {
|
||||
time = (time ?? 100);
|
||||
return new Promise((resolve) => {
|
||||
setTimeout(() => {
|
||||
resolve(true);
|
||||
}, time);
|
||||
});
|
||||
time = (time ?? 100);
|
||||
return new Promise((resolve) => {
|
||||
setTimeout(() => {
|
||||
resolve(true);
|
||||
}, time);
|
||||
});
|
||||
}
|
||||
|
||||
function formatSize(size) {
|
||||
size = isNaN(size) ? 0 : (size??0);
|
||||
size = Math.max(size, 0);
|
||||
let k = 1024,
|
||||
i = size === 0 ? 0 : Math.floor(Math.log(size) / Math.log(k)),
|
||||
units = ['B', 'KB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB'],
|
||||
result = size / Math.pow(k, i);
|
||||
return (Math.round(result * 100) / 100) + ' ' + units[i];
|
||||
size = isNaN(size) ? 0 : (size??0);
|
||||
size = Math.max(size, 0);
|
||||
let k = 1024,
|
||||
i = size === 0 ? 0 : Math.floor(Math.log(size) / Math.log(k)),
|
||||
units = ['B', 'KB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB'],
|
||||
result = size / Math.pow(k, i);
|
||||
return (Math.round(result * 100) / 100) + ' ' + units[i];
|
||||
}
|
||||
|
||||
function tsToTime(ts) {
|
||||
if (isNaN(ts)) return 'Unknown';
|
||||
let hours = Math.floor(ts / 3600);
|
||||
ts %= 3600;
|
||||
let minutes = Math.floor(ts / 60);
|
||||
return `${String(hours) + i18n.t('COMMON.HOURS') + ' ' + String(minutes) + i18n.t('COMMON.MINUTES')}`;
|
||||
if (isNaN(ts)) return 'Unknown';
|
||||
let hours = Math.floor(ts / 3600);
|
||||
ts %= 3600;
|
||||
let minutes = Math.floor(ts / 60);
|
||||
return `${String(hours) + i18n.t('COMMON.HOURS') + ' ' + String(minutes) + i18n.t('COMMON.MINUTES')}`;
|
||||
}
|
||||
|
||||
function getBaseURL(ws, suffix) {
|
||||
if (location.protocol === 'https:') {
|
||||
let scheme = ws ? 'wss' : 'https';
|
||||
return scheme + `://${location.host}${location.pathname}${suffix}`;
|
||||
}
|
||||
let scheme = ws ? 'ws' : 'http';
|
||||
return scheme + `://${location.host}${location.pathname}${suffix}`;
|
||||
if (location.protocol === 'https:') {
|
||||
let scheme = ws ? 'wss' : 'https';
|
||||
return scheme + `://${location.host}${location.pathname}${suffix}`;
|
||||
}
|
||||
let scheme = ws ? 'ws' : 'http';
|
||||
return scheme + `://${location.host}${location.pathname}${suffix}`;
|
||||
}
|
||||
|
||||
function genRandHex(len) {
|
||||
return [...Array(len)].map(() => Math.floor(Math.random() * 16).toString(16)).join('');
|
||||
return [...Array(len)].map(() => Math.floor(Math.random() * 16).toString(16)).join('');
|
||||
}
|
||||
|
||||
function post(url, data, ext) {
|
||||
let form = document.createElement('form');
|
||||
form.action = url;
|
||||
form.method = 'POST';
|
||||
form.target = '_self';
|
||||
let form = document.createElement('form');
|
||||
form.action = url;
|
||||
form.method = 'POST';
|
||||
form.target = '_self';
|
||||
|
||||
for (const key in ext) {
|
||||
form[key] = ext[key];
|
||||
}
|
||||
for (const key in data) {
|
||||
if (Array.isArray(data[key])) {
|
||||
for (let i = 0; i < data[key].length; i++) {
|
||||
let input = document.createElement('input');
|
||||
input.name = key;
|
||||
input.value = data[key][i];
|
||||
form.appendChild(input);
|
||||
}
|
||||
continue;
|
||||
}
|
||||
let input = document.createElement('input');
|
||||
input.name = key;
|
||||
input.value = data[key];
|
||||
form.appendChild(input);
|
||||
}
|
||||
for (const key in ext) {
|
||||
form[key] = ext[key];
|
||||
}
|
||||
for (const key in data) {
|
||||
if (Array.isArray(data[key])) {
|
||||
for (let i = 0; i < data[key].length; i++) {
|
||||
let input = document.createElement('input');
|
||||
input.name = key;
|
||||
input.value = data[key][i];
|
||||
form.appendChild(input);
|
||||
}
|
||||
continue;
|
||||
}
|
||||
let input = document.createElement('input');
|
||||
input.name = key;
|
||||
input.value = data[key];
|
||||
form.appendChild(input);
|
||||
}
|
||||
|
||||
document.body.appendChild(form).submit();
|
||||
form.remove();
|
||||
document.body.appendChild(form).submit();
|
||||
form.remove();
|
||||
}
|
||||
|
||||
function translate(text) {
|
||||
return text.replace(/\$\{i18n\|([a-zA-Z0-9_.]+)\}/g, (match, key) => {
|
||||
return i18n.t(key);
|
||||
});
|
||||
return text.replace(/\$\{i18n\|([a-zA-Z0-9_.]+)\}/g, (match, key) => {
|
||||
return i18n.t(key);
|
||||
});
|
||||
}
|
||||
|
||||
function preventClose(e) {
|
||||
e.preventDefault();
|
||||
e.returnValue = '';
|
||||
return '';
|
||||
e.preventDefault();
|
||||
e.returnValue = '';
|
||||
return '';
|
||||
}
|
||||
|
||||
function catchBlobReq(err) {
|
||||
let res = err.response;
|
||||
if ((res?.data?.type ?? '').startsWith('application/json')) {
|
||||
let data = res?.data ?? {};
|
||||
data.text().then((str) => {
|
||||
let data = {};
|
||||
try {
|
||||
data = JSON.parse(str);
|
||||
} catch (e) { }
|
||||
message.warn(data.msg ? translate(data.msg) : i18n.t('COMMON.REQUEST_FAILED'));
|
||||
});
|
||||
}
|
||||
let res = err.response;
|
||||
if ((res?.data?.type ?? '').startsWith('application/json')) {
|
||||
let data = res?.data ?? {};
|
||||
data.text().then((str) => {
|
||||
let data = {};
|
||||
try {
|
||||
data = JSON.parse(str);
|
||||
} catch (e) { }
|
||||
message.warn(data.msg ? translate(data.msg) : i18n.t('COMMON.REQUEST_FAILED'));
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
function hex2buf(hex) {
|
||||
if (typeof hex !== 'string') {
|
||||
return new Uint8Array([]);
|
||||
}
|
||||
let list = hex.match(/.{1,2}/g);
|
||||
if (list === null) {
|
||||
return new Uint8Array([]);
|
||||
}
|
||||
return new Uint8Array(list.map(byte => parseInt(byte, 16)));
|
||||
if (typeof hex !== 'string') {
|
||||
return new Uint8Array([]);
|
||||
}
|
||||
let list = hex.match(/.{1,2}/g);
|
||||
if (list === null) {
|
||||
return new Uint8Array([]);
|
||||
}
|
||||
return new Uint8Array(list.map(byte => parseInt(byte, 16)));
|
||||
}
|
||||
|
||||
function ab2str(buffer) {
|
||||
const array = new Uint8Array(buffer);
|
||||
let out, i, len, c;
|
||||
let char2, char3;
|
||||
const array = new Uint8Array(buffer);
|
||||
let out, i, len, c;
|
||||
let char2, char3;
|
||||
|
||||
out = "";
|
||||
len = array.length;
|
||||
i = 0;
|
||||
while (i < len) {
|
||||
c = array[i++];
|
||||
switch (c >> 4) {
|
||||
case 0:
|
||||
case 1:
|
||||
case 2:
|
||||
case 3:
|
||||
case 4:
|
||||
case 5:
|
||||
case 6:
|
||||
case 7:
|
||||
out += String.fromCharCode(c);
|
||||
break;
|
||||
case 12:
|
||||
case 13:
|
||||
char2 = array[i++];
|
||||
out += String.fromCharCode(((c & 0x1F) << 6) | (char2 & 0x3F));
|
||||
break;
|
||||
case 14:
|
||||
char2 = array[i++];
|
||||
char3 = array[i++];
|
||||
out += String.fromCharCode(((c & 0x0F) << 12) |
|
||||
((char2 & 0x3F) << 6) |
|
||||
((char3 & 0x3F) << 0));
|
||||
break;
|
||||
}
|
||||
}
|
||||
return out;
|
||||
out = "";
|
||||
len = array.length;
|
||||
i = 0;
|
||||
while (i < len) {
|
||||
c = array[i++];
|
||||
switch (c >> 4) {
|
||||
case 0:
|
||||
case 1:
|
||||
case 2:
|
||||
case 3:
|
||||
case 4:
|
||||
case 5:
|
||||
case 6:
|
||||
case 7:
|
||||
out += String.fromCharCode(c);
|
||||
break;
|
||||
case 12:
|
||||
case 13:
|
||||
char2 = array[i++];
|
||||
out += String.fromCharCode(((c & 0x1F) << 6) | (char2 & 0x3F));
|
||||
break;
|
||||
case 14:
|
||||
char2 = array[i++];
|
||||
char3 = array[i++];
|
||||
out += String.fromCharCode(((c & 0x0F) << 12) |
|
||||
((char2 & 0x3F) << 6) |
|
||||
((char3 & 0x3F) << 0));
|
||||
break;
|
||||
}
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
function ws2ua(wordArray) {
|
||||
const l = wordArray.sigBytes;
|
||||
const words = wordArray.words;
|
||||
const result = new Uint8Array(l);
|
||||
let i = 0, j = 0;
|
||||
while (true) {
|
||||
if (i === l)
|
||||
break;
|
||||
const w = words[j++];
|
||||
result[i++] = (w & 0xff000000) >>> 24;
|
||||
if (i === l)
|
||||
break;
|
||||
result[i++] = (w & 0x00ff0000) >>> 16;
|
||||
if (i === l)
|
||||
break;
|
||||
result[i++] = (w & 0x0000ff00) >>> 8;
|
||||
if (i === l)
|
||||
break;
|
||||
result[i++] = (w & 0x000000ff);
|
||||
}
|
||||
return result;
|
||||
const l = wordArray.sigBytes;
|
||||
const words = wordArray.words;
|
||||
const result = new Uint8Array(l);
|
||||
let i = 0, j = 0;
|
||||
while (true) {
|
||||
if (i === l)
|
||||
break;
|
||||
const w = words[j++];
|
||||
result[i++] = (w & 0xff000000) >>> 24;
|
||||
if (i === l)
|
||||
break;
|
||||
result[i++] = (w & 0x00ff0000) >>> 16;
|
||||
if (i === l)
|
||||
break;
|
||||
result[i++] = (w & 0x0000ff00) >>> 8;
|
||||
if (i === l)
|
||||
break;
|
||||
result[i++] = (w & 0x000000ff);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
function encrypt(data, secret) {
|
||||
let json = JSON.stringify(data);
|
||||
json = CryptoJS.enc.Utf8.parse(json);
|
||||
let encrypted = CryptoJS.AES.encrypt(json, secret, {
|
||||
mode: CryptoJS.mode.CTR,
|
||||
iv: secret,
|
||||
padding: CryptoJS.pad.NoPadding
|
||||
});
|
||||
return ws2ua(encrypted.ciphertext);
|
||||
let json = JSON.stringify(data);
|
||||
json = CryptoJS.enc.Utf8.parse(json);
|
||||
let encrypted = CryptoJS.AES.encrypt(json, secret, {
|
||||
mode: CryptoJS.mode.CTR,
|
||||
iv: secret,
|
||||
padding: CryptoJS.pad.NoPadding
|
||||
});
|
||||
return ws2ua(encrypted.ciphertext);
|
||||
}
|
||||
|
||||
function decrypt(data, secret) {
|
||||
data = CryptoJS.lib.WordArray.create(data);
|
||||
let decrypted = CryptoJS.AES.encrypt(data, secret, {
|
||||
mode: CryptoJS.mode.CTR,
|
||||
iv: secret,
|
||||
padding: CryptoJS.pad.NoPadding
|
||||
});
|
||||
return ab2str(ws2ua(decrypted.ciphertext).buffer);
|
||||
data = CryptoJS.lib.WordArray.create(data);
|
||||
let decrypted = CryptoJS.AES.encrypt(data, secret, {
|
||||
mode: CryptoJS.mode.CTR,
|
||||
iv: secret,
|
||||
padding: CryptoJS.pad.NoPadding
|
||||
});
|
||||
return ab2str(ws2ua(decrypted.ciphertext).buffer);
|
||||
}
|
||||
|
||||
export {post, request, waitTime, formatSize, tsToTime, getBaseURL, genRandHex, translate, preventClose, catchBlobReq, hex2buf, ab2str, ws2ua, encrypt, decrypt, orderCompare};
|
Reference in New Issue
Block a user