diff --git a/.DS_Store b/.DS_Store index 79b194b..79e8c00 100644 Binary files a/.DS_Store and b/.DS_Store differ diff --git a/.gitignore b/.gitignore index f4fa156..317860e 100644 --- a/.gitignore +++ b/.gitignore @@ -22,3 +22,4 @@ yarn-error.log /.vscode /.zed package-lock.json +*.DS_Store* diff --git a/app/.DS_Store b/app/.DS_Store index baf8fde..4529371 100644 Binary files a/app/.DS_Store and b/app/.DS_Store differ diff --git a/app/Actions/Filemanager/GetFileContentsAction.php b/app/Actions/Filemanager/GetFileContentsAction.php index d790e49..97098a6 100644 --- a/app/Actions/Filemanager/GetFileContentsAction.php +++ b/app/Actions/Filemanager/GetFileContentsAction.php @@ -36,6 +36,7 @@ class GetFileContentsAction 'text/x-typescript', // .ts, .tsx 'text/x-jsx', // .jsx, .tsx 'application/x-sh', // .sh + 'application/x-sql', // .sql ]; diff --git a/app/Actions/Filemanager/UploadFileAction.php b/app/Actions/Filemanager/UploadFileAction.php new file mode 100644 index 0000000..294b6bf --- /dev/null +++ b/app/Actions/Filemanager/UploadFileAction.php @@ -0,0 +1,44 @@ +validate([ + 'file' => 'required|file', + 'chunkIndex' => 'required|integer|min:0', + 'totalChunks' => 'required|integer|min:1', + 'originalName' => 'required|string|max:255', + 'path' => 'required|string', + ]); + + $file = $r->file('file'); + + $path = $this->path . '/' . $r->path; + + File::append($path . '/' . $r->originalName, $file->get()); + + /* $handle = fopen($file->getPathname(), 'rb'); */ + /* $destination = fopen($path . '/' . $r->originalName, 'ab'); */ + /**/ + /* if ($handle && $destination) { */ + /* stream_copy_to_stream($handle, $destination); // Append chunk data */ + /* fclose($handle); */ + /* fclose($destination); */ + /* } */ + + return response()->json([ + 'message' => 'Chunk uploaded', + 'chunkIndex' => $r->chunkIndex, + 'totalChunks' => $r->totalChunks, + 'toDestination' => $path . '/' . $r->originalName + ]); + } +} diff --git a/app/Http/Controllers/FilemanagerController.php b/app/Http/Controllers/FilemanagerController.php index aebf82c..5df9ef5 100644 --- a/app/Http/Controllers/FilemanagerController.php +++ b/app/Http/Controllers/FilemanagerController.php @@ -12,12 +12,14 @@ use App\Actions\Filemanager\GetDirectoryContentsAction; use App\Actions\Filemanager\GetFileContentsAction; use App\Actions\Filemanager\RenameFileAction; use App\Actions\Filemanager\UpdateFileContentsAction; +use App\Actions\Filemanager\UploadFileAction; use League\Flysystem\UnixVisibility\PortableVisibilityConverter; use Illuminate\Http\StreamedResponse; class FilemanagerController extends Controller { public Filesystem $filesystem; + public string $path; public function __construct() { @@ -26,6 +28,7 @@ class FilemanagerController extends Controller $adapter = new LocalFilesystemAdapter($path, null, LOCK_EX, LocalFilesystemAdapter::DISALLOW_LINKS); $this->filesystem = new Filesystem($adapter); + $this->path = $path; } public function index() @@ -62,4 +65,9 @@ class FilemanagerController extends Controller { return (new DeleteFilesAction($this->filesystem))->execute($r); } + + public function uploadFile(Request $r) + { + return (new UploadFileAction($this->path))->execute($r); + } } diff --git a/database/.DS_Store b/database/.DS_Store index b2313ca..4c2883b 100644 Binary files a/database/.DS_Store and b/database/.DS_Store differ diff --git a/public/.DS_Store b/public/.DS_Store index 3568b76..ada1f43 100644 Binary files a/public/.DS_Store and b/public/.DS_Store differ diff --git a/resources/js/Pages/Filemanager/Components/UploadFile.jsx b/resources/js/Pages/Filemanager/Components/UploadFile.jsx new file mode 100644 index 0000000..5c54f56 --- /dev/null +++ b/resources/js/Pages/Filemanager/Components/UploadFile.jsx @@ -0,0 +1,57 @@ +import { useState } from "react"; +import axios from "axios"; +import { toast } from "react-toastify"; + +import PrimaryButton from "@/Components/PrimaryButton"; +import Modal from "@/Components/Modal"; +import SecondaryButton from "@/Components/SecondaryButton"; + +const CHUNK_SIZE = 2 * 1024 * 1024; // 4MB per chunk + +const UploadFile = ({ showUploadFile, setShowUploadFile, refreshFiles, path }) => { + + const [file, setFile] = useState(null); + const [progress, setProgress] = useState(0); + + const uploadFile = async () => { + if (!file) return; + + const totalChunks = Math.ceil(file.size / CHUNK_SIZE); + let uploadedChunks = 0; + + for (let chunkIndex = 0; chunkIndex < totalChunks; chunkIndex++) { + const start = chunkIndex * CHUNK_SIZE; + const end = Math.min(start + CHUNK_SIZE, file.size); + const chunk = file.slice(start, end); + + const formData = new FormData(); + formData.append("file", chunk); + formData.append("chunkIndex", chunkIndex); + formData.append("totalChunks", totalChunks); + formData.append("originalName", file.name); + formData.append("path", path); + + await axios.post("/filemanager/upload-file", formData); + + uploadedChunks++; + setProgress(Math.round((uploadedChunks / totalChunks) * 100)); + } + + refreshFiles(path); + setShowUploadFile(false); + toast('File uploaded successfully', { type: 'success' }); + }; + + return ( + setShowUploadFile(false)}> +
+ setFile(e.target.files[0])} /> + Upload + setShowUploadFile(false)}>Cancel +

Upload Progress: {progress}%

+
+
+ ); +} + +export default UploadFile; diff --git a/resources/js/Pages/Filemanager/Filemanager.jsx b/resources/js/Pages/Filemanager/Filemanager.jsx index b0c37c0..4268b6e 100644 --- a/resources/js/Pages/Filemanager/Filemanager.jsx +++ b/resources/js/Pages/Filemanager/Filemanager.jsx @@ -11,6 +11,7 @@ import CreateFile from './Components/CreateFile'; import EditFile from './Components/EditFile'; import DeleteFiles from './Components/DeleteFiles'; import RenameFile from './Components/RenameFile'; +import UploadFile from './Components/UploadFile'; import { LuFolderPlus } from "react-icons/lu"; import { LuFilePlus2 } from "react-icons/lu"; @@ -35,6 +36,7 @@ const Filemanager = () => { const [editFile, setEditFile] = useState(false); const [showConfirmDelete, setShowConfirmDelete] = useState(false); const [renameFile, setRenameFile] = useState(false); + const [showUploadFile, setShowUploadFile] = useState(false); useEffect(() => { cdIntoPath(path); @@ -159,7 +161,7 @@ const Filemanager = () => {
- @@ -296,6 +298,13 @@ const Filemanager = () => { path={path} /> + + ); } diff --git a/routes/web.php b/routes/web.php index 3e296a2..ec4ac98 100644 --- a/routes/web.php +++ b/routes/web.php @@ -28,6 +28,7 @@ Route::patch('/filemanager/update-file-contents', [FilemanagerController::class, Route::post('/filemanager/create-file', [FilemanagerController::class, 'createFile'])->middleware(['auth'])->name('filemanager.createFile'); Route::patch('/filemanager/rename-file', [FilemanagerController::class, 'renameFile'])->middleware(['auth'])->name('filemanager.renameFile'); Route::post('/filemanager/delete-files', [FilemanagerController::class, 'deleteFiles'])->middleware(['auth'])->name('filemanager.deleteFiles'); +Route::post('/filemanager/upload-file', [FilemanagerController::class, 'uploadFile'])->middleware(['auth'])->name('filemanager.uploadFile'); // Stats History Route::get('/stats/history', [StatsHistoryController::class, 'cpuAndMemory'])->middleware(['auth', AdminMiddleware::class])->name('stats.history');