diff --git a/CHANGELOG.md b/CHANGELOG.md index 729af84..bbea6bc 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,4 +1,3 @@ -# Changelog All notable changes to DockerFlex will be documented in this file. diff --git a/README.md b/README.md index 47037cd..c8c6ccc 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,6 @@
DockerFlex Logo - # DockerFlex > The missing file manager for Docker containers diff --git a/features.md b/features.md index 0933ee9..dbc060a 100644 --- a/features.md +++ b/features.md @@ -1,4 +1,3 @@ -# DockerFlex Features ## Security & Authentication diff --git a/frontend/src/App.js b/frontend/src/App.js index dda6c01..f488be6 100644 --- a/frontend/src/App.js +++ b/frontend/src/App.js @@ -1,4 +1,4 @@ -import React, { useEffect, useState } from 'react'; +import React, { useEffect, useState, lazy, Suspense } from 'react'; import axios from 'axios'; import { AppBar, @@ -59,18 +59,6 @@ import MuiAlert from '@material-ui/lab/Alert'; import Menu from '@material-ui/core/Menu'; import MenuItem from '@material-ui/core/MenuItem'; import NoteAddIcon from '@material-ui/icons/NoteAdd'; -import CodeMirror from '@uiw/react-codemirror'; -import { javascript } from '@codemirror/lang-javascript'; -import { oneDark } from '@codemirror/theme-one-dark'; -import { json } from '@codemirror/lang-json'; -import { yaml } from '@codemirror/lang-yaml'; -import { xml } from '@codemirror/lang-xml'; -import { sql } from '@codemirror/lang-sql'; -import { python } from '@codemirror/lang-python'; -import { php } from '@codemirror/lang-php'; -import { markdown } from '@codemirror/lang-markdown'; -import { css } from '@codemirror/lang-css'; -import { html } from '@codemirror/lang-html'; import { linter, lintGutter } from "@codemirror/lint"; import jsonlint from 'jsonlint-mod'; import yamlLint from 'yaml-lint'; @@ -620,7 +608,7 @@ const useStyles = makeStyles((theme) => ({ margin: 0, }, '& .MuiButton-label > span:last-child': { - display: 'none', + display: 'none', }, }, }, @@ -769,7 +757,7 @@ const useStyles = makeStyles((theme) => ({ left: '50%', marginTop: -12, marginLeft: -12, - color: '#ffffff !important', + color: '#ffffff !important', }, saveButtonWrapper: { position: 'relative', @@ -1029,7 +1017,7 @@ function App() { const [searchQuery, setSearchQuery] = useState(''); const [mobileSearchDialog, setMobileSearchDialog] = useState(false); const [saving, setSaving] = useState(false); - const classes = useStyles({ saving }); + const classes = useStyles({ saving }); const [originalContent, setOriginalContent] = useState(''); const [showUnsavedDialog, setShowUnsavedDialog] = useState(false); @@ -1061,7 +1049,7 @@ function App() { if (entity.execute) value += 1; return value; }; - + return `${calculate(perms.owner)}${calculate(perms.group)}${calculate(perms.others)}`; }; @@ -1071,7 +1059,7 @@ function App() { write: (value & 2) === 2, execute: (value & 1) === 1 }); - + const digits = numeric.toString().padStart(3, '0'); return { owner: parseBits(parseInt(digits[0])), @@ -1084,19 +1072,19 @@ function App() { const handlePermissionsClick = async () => { if (!selectedFile) return; - + try { setPermissionsLoading(true); const response = await axios.get( `${INTERNAL_API_URL}/api/containers/${selectedContainer.Id}/permissions`, { params: { path: `${currentPath}/${selectedFile.name}` } } ); - + const mode = response.data.mode.replace(/[^\d]/g, '').slice(-3); if (!mode || !/^[0-7]{3}$/.test(mode)) { throw new Error('Invalid permissions format received'); } - + const newPermissions = parseNumericPermissions(mode); setPermissions(newPermissions); setPermissionsDialog(true); @@ -1109,9 +1097,9 @@ function App() { const handlePermissionsSave = async () => { if (!selectedFile) return; - + try { - setPermissionsLoading(true); + setPermissionsLoading(true); await axios.put( `${INTERNAL_API_URL}/api/containers/${selectedContainer.Id}/permissions`, { @@ -1119,13 +1107,13 @@ function App() { mode: calculateNumericPermissions(permissions) } ); - + setPermissionsDialog(false); showSuccessMessage('Permissions updated successfully'); await fetchFiles(selectedContainer.Id, currentPath); - setPermissionsLoading(false); + setPermissionsLoading(false); } catch (error) { - setPermissionsLoading(false); + setPermissionsLoading(false); showErrorMessage('Error updating permissions: ' + error.message); } }; @@ -1167,7 +1155,7 @@ function App() { setIsAuthenticated(true); setShowAuthDialog(false); setAuthError(''); - + await fetchContainers(); } } catch (error) { @@ -1190,13 +1178,13 @@ function App() { const restorePreviousState = async () => { const savedContainer = localStorage.getItem('selectedContainer'); const savedPath = localStorage.getItem('currentPath'); - + if (savedContainer) { try { const parsedContainer = JSON.parse(savedContainer); setSelectedContainer(parsedContainer); setOpenDialog(true); - + if (savedPath) { setCurrentPath(savedPath); await fetchFiles(parsedContainer.Id, savedPath); @@ -1214,7 +1202,7 @@ function App() { try { const response = await axios.get(`${INTERNAL_API_URL}/api/containers`); setContainers(response.data); - + await restorePreviousState(); } catch (error) { console.error('Error fetching containers:', error); @@ -1308,7 +1296,7 @@ function App() { if (elementRect.top < topOffset) { selectedElement.scrollIntoView(true); - fileList.scrollTop -= (toolbarHeight + 40); + fileList.scrollTop -= (toolbarHeight + 40); } selectedElement.scrollIntoView({ behavior: 'smooth', @@ -1334,7 +1322,7 @@ function App() { useEffect(() => { const handleKeyDown = (e) => { if (permissionsDialog) return; - + if (!openDialog || openEditor) return; if (e.key === 'Enter' && selectedFile) { @@ -1371,7 +1359,7 @@ function App() { const response = await axios.get( `${INTERNAL_API_URL}/api/containers/${container.Id}/files`, - { params: { path: '/' } } + { params: { path: '/' } } ); if (response.status === 200) { @@ -1464,7 +1452,7 @@ function App() { if (response.status === 200) { setFileContent(response.data); - setOriginalContent(response.data); + setOriginalContent(response.data); setSelectedFile(file); setOpenEditor(true); window.history.pushState({ type: 'editor', path: currentPath }, ''); @@ -1970,7 +1958,7 @@ function App() {
- {} + { } {uploadQueue.length > 0 && ( <> @@ -2292,7 +2280,7 @@ function App() { setDragOverItem(null); if (!draggedItem || draggedItem.name === targetFolder.name) { - setDraggedItem(null); + setDraggedItem(null); return; } @@ -2313,7 +2301,7 @@ function App() { showErrorMessage('Error moving file: ' + (error.response?.data?.error || error.message)); } - setDraggedItem(null); + setDraggedItem(null); }; const handleDragEnd = () => { @@ -2381,24 +2369,24 @@ function App() { fullWidth onKeyDown={handlePermissionsKeyDown} > - File Permissions - {selectedFile?.name} - {} + { } return ( {isLoading ? ( - @@ -2483,7 +2471,7 @@ function App() { - + Host: {hostInfo} @@ -2604,9 +2592,9 @@ function App() { {currentPath} - {} + { }
- {} + { }
- {} + { }
- {} + { }
- {} + { }
- {} + { }
- {} + { }
- {} + { }
- {} + { }
- {} + { } - {} + { }
- setFileContent(value)} - onMouseMove={handleMouseMove} - extensions={[ - getFileLanguage(selectedFile?.name || '') || [], - lintGutter(), - createLinter(selectedFile?.name || '') - ].filter(Boolean)} - className={classes.editor} - basicSetup={{ - lineNumbers: true, - highlightActiveLineGutter: true, - highlightSpecialChars: true, - history: true, - foldGutter: true, - drawSelection: true, - dropCursor: true, - allowMultipleSelections: true, - indentOnInput: true, - bracketMatching: true, - closeBrackets: true, - autocompletion: true, - rectangularSelection: true, - crosshairCursor: true, - highlightActiveLine: true, - highlightSelectionMatches: true, - closeBracketsKeymap: true, - defaultKeymap: true, - searchKeymap: true, - historyKeymap: true, - foldKeymap: true, - completionKeymap: true, - lintKeymap: true, - }} - /> + + +
+ }> + setFileContent(value)} + onMouseMove={handleMouseMove} + extensions={[ + getFileLanguage(selectedFile?.name || '') || [], + lintGutter(), + createLinter(selectedFile?.name || '') + ].filter(Boolean)} + className={classes.editor} + basicSetup={{ + lineNumbers: true, + highlightActiveLineGutter: true, + highlightSpecialChars: true, + history: true, + foldGutter: true, + drawSelection: true, + dropCursor: true, + allowMultipleSelections: true, + indentOnInput: true, + bracketMatching: true, + closeBrackets: true, + autocompletion: true, + rectangularSelection: true, + crosshairCursor: true, + highlightActiveLine: true, + highlightSelectionMatches: true, + closeBracketsKeymap: true, + defaultKeymap: true, + searchKeymap: true, + historyKeymap: true, + foldKeymap: true, + completionKeymap: true, + lintKeymap: true, + }} + /> +
@@ -3269,7 +3270,7 @@ function App() { - {} + { } setPermissionsDialog(false)} @@ -3278,7 +3279,7 @@ function App() { fullWidth onKeyDown={handlePermissionsKeyDown} > - @@ -3293,7 +3294,7 @@ function App() { {['owner', 'group', 'others'].map((entity) => ( - @@ -3308,9 +3309,9 @@ function App() { onChange={(e) => { const newPermissions = { ...permissions, - [entity]: { - ...permissions[entity], - read: e.target.checked + [entity]: { + ...permissions[entity], + read: e.target.checked } }; setPermissions(newPermissions); @@ -3331,9 +3332,9 @@ function App() { onChange={(e) => { const newPermissions = { ...permissions, - [entity]: { - ...permissions[entity], - write: e.target.checked + [entity]: { + ...permissions[entity], + write: e.target.checked } }; setPermissions(newPermissions); @@ -3354,9 +3355,9 @@ function App() { onChange={(e) => { const newPermissions = { ...permissions, - [entity]: { - ...permissions[entity], - execute: e.target.checked + [entity]: { + ...permissions[entity], + execute: e.target.checked } }; setPermissions(newPermissions); @@ -3386,7 +3387,7 @@ function App() { } }} className={classes.newItemTextField} - inputProps={{ + inputProps={{ maxLength: 3, style: { color: '#e6edf3' }, type: 'text', diff --git a/frontend/vite.config.mjs b/frontend/vite.config.mjs index f4620f1..d1c7a86 100644 --- a/frontend/vite.config.mjs +++ b/frontend/vite.config.mjs @@ -47,5 +47,47 @@ export default defineConfig({ '.js': 'jsx', }, }, + }, + build: { + rollupOptions: { + output: { + manualChunks: { + 'vendor': [ + 'react', + 'react-dom', + '@material-ui/core', + '@material-ui/icons', + '@material-ui/lab', + 'axios' + ], + 'codemirror': [ + '@uiw/react-codemirror', + '@codemirror/lang-javascript', + '@codemirror/theme-one-dark', + '@codemirror/lang-json', + '@codemirror/lang-yaml', + '@codemirror/lang-xml', + '@codemirror/lang-sql', + '@codemirror/lang-python', + '@codemirror/lang-php', + '@codemirror/lang-markdown', + '@codemirror/lang-css', + '@codemirror/lang-html', + '@codemirror/legacy-modes', + '@codemirror/lang-go', + '@codemirror/lint' + ] + }, + chunkSizeWarningLimit: 1000 + } + }, + minify: 'terser', + terserOptions: { + compress: { + drop_console: true, + drop_debugger: true + } + }, + sourcemap: false } }); \ No newline at end of file