From 182a0f2cb6f78675e6c25cd9a291cbeba38facc3 Mon Sep 17 00:00:00 2001 From: Bluefox Date: Fri, 15 Nov 2024 20:39:14 +0000 Subject: [PATCH] Build optimization (#2809) --- README.md | 1 + .../src/Components/FileViewer.tsx | 18 +++--- .../admin/src-admin/src/components/Editor.tsx | 6 ++ packages/admin/src/lib/checkLinuxPass.ts | 44 +++++++++++---- packages/admin/src/lib/web.ts | 56 +++++++++++-------- packages/admin/tasks.js | 14 +++++ .../src/JsonConfigComponent/ConfigGeneric.tsx | 2 +- 7 files changed, 96 insertions(+), 45 deletions(-) diff --git a/README.md b/README.md index f7d8ff76e..ab54ffe32 100644 --- a/README.md +++ b/README.md @@ -90,6 +90,7 @@ The icons may not be reused in other projects without the proper flaticon licens ### **WORK IN PROGRESS** - (@GermanBluefox) Some GUI packages were updated. +- (@GermanBluefox) Improved file viewer. Added icons viewer ### 7.3.1 (2024-11-14) diff --git a/packages/adapter-react-v5/src/Components/FileViewer.tsx b/packages/adapter-react-v5/src/Components/FileViewer.tsx index 8c666eed1..c15822a4d 100644 --- a/packages/adapter-react-v5/src/Components/FileViewer.tsx +++ b/packages/adapter-react-v5/src/Components/FileViewer.tsx @@ -46,7 +46,7 @@ const styles: Record = { }; export const EXTENSIONS = { - images: ['png', 'jpg', 'svg', 'jpeg', 'bmp', 'gif', 'apng', 'avif', 'webp'], + images: ['png', 'jpg', 'svg', 'jpeg', 'bmp', 'gif', 'apng', 'avif', 'webp', 'ico'], code: ['js', 'json', 'json5', 'md'], txt: ['log', 'txt', 'html', 'css', 'xml', 'ics'], audio: ['mp3', 'wav', 'ogg', 'acc'], @@ -55,7 +55,7 @@ export const EXTENSIONS = { function bufferToBase64(buffer: Buffer, isFull?: boolean): string { let binary = ''; - const bytes = new Uint8Array(buffer); + const bytes = new Uint8Array((buffer as unknown as { data: number[]; type: 'Buffer' })?.data || buffer); const len = bytes.byteLength; for (let i = 0; i < len && (isFull || i < 50); i++) { binary += String.fromCharCode(bytes[i]); @@ -121,9 +121,9 @@ export class FileViewerClass extends Component this.props.socket .readFile(adapter, name) - .then((data: { data: Buffer; type: string } | { file: string; mimeType: string }) => { - let fileData = ''; - if ((data as { file: string; mimeType: string }).file !== undefined) { + .then((data: { file: string | Buffer; mimeType: string }) => { + let fileData: string | Buffer = ''; + if (data.file !== undefined) { fileData = (data as { file: string; mimeType: string }).file; } @@ -132,20 +132,18 @@ export class FileViewerClass extends Component ext: this.state.ext, }; // try to detect valid extension - if ((data as { data: Buffer; type: string }).type === 'Buffer') { + if ((fileData as unknown as { data: Buffer; type: string }).type === 'Buffer') { if (name.toLowerCase().endsWith('.json5')) { newState.ext = 'json5'; newState.copyPossible = true; try { - fileData = atob(bufferToBase64((data as { data: Buffer; type: string }).data, true)); + fileData = atob(bufferToBase64(fileData as unknown as Buffer, true)); } catch { console.error('Cannot convert base64 to string'); fileData = ''; } } else { - const ext = Utils.detectMimeType( - bufferToBase64((data as { data: Buffer; type: string }).data), - ); + const ext = Utils.detectMimeType(bufferToBase64(fileData as unknown as Buffer)); if (ext) { newState.ext = ext; newState.copyPossible = EXTENSIONS.code.includes(ext) || EXTENSIONS.txt.includes(ext); diff --git a/packages/admin/src-admin/src/components/Editor.tsx b/packages/admin/src-admin/src/components/Editor.tsx index d5cc6619d..74919a383 100644 --- a/packages/admin/src-admin/src/components/Editor.tsx +++ b/packages/admin/src-admin/src/components/Editor.tsx @@ -2,7 +2,13 @@ import React, { Component, type JSX } from 'react'; import AceEditor from 'react-ace'; import 'ace-builds/src-min-noconflict/mode-json'; +import 'ace-builds/src-min-noconflict/mode-json5'; +import 'ace-builds/src-min-noconflict/mode-xml'; +import 'ace-builds/src-min-noconflict/mode-html'; import 'ace-builds/src-min-noconflict/worker-json'; +import 'ace-builds/src-min-noconflict/worker-javascript'; +import 'ace-builds/src-min-noconflict/worker-xml'; +import 'ace-builds/src-min-noconflict/worker-html'; import 'ace-builds/src-min-noconflict/theme-clouds_midnight'; import 'ace-builds/src-min-noconflict/theme-chrome'; import 'ace-builds/src-min-noconflict/ext-language_tools'; diff --git a/packages/admin/src/lib/checkLinuxPass.ts b/packages/admin/src/lib/checkLinuxPass.ts index 0e1fe6c51..502010bba 100644 --- a/packages/admin/src/lib/checkLinuxPass.ts +++ b/packages/admin/src/lib/checkLinuxPass.ts @@ -31,19 +31,33 @@ function checkLinuxPassword(login: string, password: string): Promise { try { const su = spawn('su', [login]); let result = false; - let responseTimeout = setTimeout(() => { + let responseTimeout: NodeJS.Timeout | null = setTimeout(() => { responseTimeout = null; su.kill(); }, 3000); function _checkPassword(data: string): void { + if (!responseTimeout) { + return; + } data = data.replace(/\r/g, ' ').replace(/\n/g, ' ').trim(); console.log(`[STDX] "${data}"`); if (data.endsWith(':')) { - su.stdin.write(`${password}\n`); + try { + su.stdin?.write(`${password}\n`); + } catch { + return; + } setTimeout(() => { + if (!responseTimeout) { + return; + } console.log(`[LOG] write whoami`); - su.stdin.write(`whoami\n`); + try { + su.stdin?.write(`whoami\n`); + } catch { + // ignore + } }, 50); } else if (data === login) { result = true; @@ -55,21 +69,31 @@ function checkLinuxPassword(login: string, password: string): Promise { } // Listen for data on stdout - su.stdout.on('data', data => _checkPassword(data.toString())); + su.stdout.on('data', data => { + if (data && responseTimeout) { + _checkPassword(data.toString()); + } + }); // Listen for data on stderr - su.stderr.on('data', data => _checkPassword(data.toString())); + su.stderr.on('data', data => { + if (data && responseTimeout) { + _checkPassword(data.toString()); + } + }); // Listen for the close event su.on('close', () => { console.log(`[LOG] -------- closed with result: ${result}\n`); - responseTimeout && clearTimeout(responseTimeout); - responseTimeout = null; + if (responseTimeout) { + clearTimeout(responseTimeout); + responseTimeout = null; + } resolve(result); }); - } catch (e: any) { - console.error(`[LOG] -------- Error by execution: ${e.message}\n`); - reject(new Error(e)); + } catch (e: unknown) { + console.error(`[LOG] -------- Error by execution: ${(e as Error).message}\n`); + reject(new Error(e as string)); } }); } diff --git a/packages/admin/src/lib/web.ts b/packages/admin/src/lib/web.ts index 25fa0f1c8..1838d3d38 100644 --- a/packages/admin/src/lib/web.ts +++ b/packages/admin/src/lib/web.ts @@ -791,35 +791,43 @@ class Web { return; } - this.adapter.sendToHost(`system.host.${host}`, 'getLogFile', { fileName, transport }, result => { - const _result = result as { error?: string; data?: string; size?: number; gz?: boolean }; - if (!_result || _result.error) { - res.status(404).send(get404Page(`File ${escapeHtml(fileName)} not found`)); - } else { - if (_result.gz) { - if (_result.size > 1024 * 1024) { - res.header('Content-Type', 'application/gzip'); - res.send(_result.data); - } else { - try { - this.unzipFile(fileName, _result.data, res); - } catch (e) { + this.adapter.sendToHost( + `system.host.${host}`, + 'getLogFile', + { filename: fileName, transport }, + result => { + const _result = result as { error?: string; data?: string; size?: number; gz?: boolean }; + if (!_result || _result.error) { + if (_result.error) { + this.adapter.log.warn(`Cannot read log file ${fileName}: ${_result.error}`); + } + res.status(404).send(get404Page(`File ${escapeHtml(fileName)} not found`)); + } else { + if (_result.gz) { + if (_result.size > 1024 * 1024) { res.header('Content-Type', 'application/gzip'); res.send(_result.data); - this.adapter.log.error(`Cannot extract file ${fileName}: ${e}`); + } else { + try { + this.unzipFile(fileName, _result.data, res); + } catch (e) { + res.header('Content-Type', 'application/gzip'); + res.send(_result.data); + this.adapter.log.error(`Cannot extract file ${fileName}: ${e}`); + } } + } else if (_result.data === undefined || _result.data === null) { + res.status(404).send(get404Page(`File ${escapeHtml(fileName)} not found`)); + } else if (_result.size > 2 * 1024 * 1024) { + res.header('Content-Type', 'text/plain'); + res.send(_result.data); + } else { + res.header('Content-Type', 'text/html'); + res.send(this.decorateLogFile(fileName, _result.data)); } - } else if (_result.data === undefined || _result.data === null) { - res.status(404).send(get404Page(`File ${escapeHtml(fileName)} not found`)); - } else if (_result.size > 2 * 1024 * 1024) { - res.header('Content-Type', 'text/plain'); - res.send(_result.data); - } else { - res.header('Content-Type', 'text/html'); - res.send(this.decorateLogFile(fileName, _result.data)); } - } - }); + }, + ); } else { parts = parts.splice(2); const transport = parts.shift(); diff --git a/packages/admin/tasks.js b/packages/admin/tasks.js index 4287de405..888f3c33b 100644 --- a/packages/admin/tasks.js +++ b/packages/admin/tasks.js @@ -112,6 +112,20 @@ function copyAllFiles() { copyFiles(`${srcRx}build/static/js/**/*.js`, `${dest}static/js`, { replace: [{ find: 's.p+"static/media', text: '"./static/media' }], }); + copyFiles( + [ + `${srcRx}node_modules/ace-builds/src-min-noconflict/worker-json.js`, + `${srcRx}node_modules/ace-builds/src-min-noconflict/worker-html.js`, + `${srcRx}node_modules/ace-builds/src-min-noconflict/worker-xml.js`, + `${srcRx}node_modules/ace-builds/src-min-noconflict/worker-yaml.js`, + `${srcRx}node_modules/ace-builds/src-min-noconflict/worker-javascript.js`, + `${srcRx}node_modules/ace-builds/src-min-noconflict/worker-css.js`, + `${srcRx}node_modules/ace-builds/src-min-noconflict/snippets/html.js`, + `${srcRx}node_modules/ace-builds/src-min-noconflict/snippets/css.js`, + `${srcRx}node_modules/ace-builds/src-min-noconflict/snippets/javascript.js`, + ], + `${dest}static/js`, + ); } async function configCSS() { diff --git a/packages/jsonConfig/src/JsonConfigComponent/ConfigGeneric.tsx b/packages/jsonConfig/src/JsonConfigComponent/ConfigGeneric.tsx index 13120f645..9bc4eb59f 100644 --- a/packages/jsonConfig/src/JsonConfigComponent/ConfigGeneric.tsx +++ b/packages/jsonConfig/src/JsonConfigComponent/ConfigGeneric.tsx @@ -312,7 +312,7 @@ export default class ConfigGeneric< if (typeof part === 'string' && typeof data[part] === 'object') { return ConfigGeneric.getValue(data[part], attr); } - return null; + return undefined; } static setValue(data: Record, attr: string | string[], value: any): void {