From 8cae541c498466fb6fce5a2b2652bdd5decc8a78 Mon Sep 17 00:00:00 2001 From: slayernominee Date: Sun, 7 Jan 2024 17:23:48 +0100 Subject: [PATCH] renaming files work (still not as i like it) --- app/dashboard/files/columns.tsx | 215 +++++++++++++++++--------------- app/dashboard/files/page.tsx | 48 ++++++- dash/src/files.rs | 35 ++++++ dash/src/main.rs | 4 + 4 files changed, 198 insertions(+), 104 deletions(-) diff --git a/app/dashboard/files/columns.tsx b/app/dashboard/files/columns.tsx index 21d76b5..90142aa 100644 --- a/app/dashboard/files/columns.tsx +++ b/app/dashboard/files/columns.tsx @@ -4,115 +4,130 @@ import { ColumnDef } from "@tanstack/react-table" import { ArrowUpDown, MoreHorizontal } from "lucide-react" import { Button } from "@/components/ui/button" import { Checkbox } from "@/components/ui/checkbox" +import { useRef } from "react" // This type is used to define the shape of our data. // You can use a Zod schema here if you want. export type File = { - name: string, - type: "file" | "folder", - modified: number + name: string, + type: "file" | "folder", + modified: number } -export const columns: ColumnDef[] = [ - { - id: "select", - header: ({ table }) => ( - { + e.preventDefault() + if (e.key == 'Enter') { + e.preventDefault() + saveNewFileName(e.target.innerText) + } + } + + const columns: ColumnDef[] = [ + { + id: "select", + header: ({ table }) => ( + table.toggleAllPageRowsSelected(!!value)} aria-label="Select all" - /> - ), - cell: ({ row }) => ( - row.toggleSelected(!!value)} - aria-label="Select row" - /> - ), - enableSorting: false, - enableHiding: false, - }, - { - accessorKey: "name", - header: ({ column }) => { - return ( - - ) - }, - cell: ({ row }) => { - const name: string = row.getValue("name") - - return ( -
{ name }
- ) - }, - - }, - { - accessorKey: "type", - header: ({ column }) => { - return ( - - ) - } - }, - { - accessorKey: "modified", - header: ({ column }) => { - return ( -
- -
- ) - }, - cell: ({ row }) => { - const modified = parseFloat(row.getValue("modified")) - - let formatted: number | string = 0 - - if (modified >= 365 * 24 * 60 * 60 * 1000) { // 365d - formatted = Math.round(modified / 1000 / 60 / 60 / 24 / 365) - formatted = `${formatted}y` - } else if (modified >= 7 * 24 * 60 * 60 * 1000) { // 365d - formatted = Math.round(modified / 1000 / 60 / 60 / 24 / 7) - formatted = `${formatted}w` - } else if (modified >= 24 * 60 * 60 * 1000) { // 24h - formatted = Math.round(modified / 1000 / 60 / 60 / 24) - formatted = `${formatted}d` - } else if (modified >= 60 * 60 * 1000) { // 60min - formatted = Math.round(modified / 1000 / 60 / 60) - formatted = `${formatted}h` - } else if (modified >= 60 * 1000) { // 60s - formatted = Math.round(modified / 1000 / 60) - formatted = `${formatted}m` - } else { - formatted = Math.round(modified / 1000) - formatted = `${formatted}s` - } - - return
{formatted} ago
- }, - }, -] + /> + ), + cell: ({ row }) => ( + row.toggleSelected(!!value)} + aria-label="Select row" + /> + ), + enableSorting: false, + enableHiding: false, + }, + { + accessorKey: "name", + header: ({ column }) => { + return ( + + ) + }, + cell: ({ row }) => { + const name: string = row.getValue("name") + + return ( + {if (editableRowId == row.index) {e.preventDefault()} }} contentEditable={editableRowId == row.index}>{ name } + ) + }, + + }, + { + accessorKey: "type", + header: ({ column }) => { + return ( + + ) + } + }, + { + accessorKey: "modified", + header: ({ column }) => { + return ( +
+ +
+ ) + }, + cell: ({ row }) => { + const modified = parseFloat(row.getValue("modified")) + + let formatted: number | string = 0 + + if (modified >= 365 * 24 * 60 * 60 * 1000) { // 365d + formatted = Math.round(modified / 1000 / 60 / 60 / 24 / 365) + formatted = `${formatted}y` + } else if (modified >= 7 * 24 * 60 * 60 * 1000) { // 365d + formatted = Math.round(modified / 1000 / 60 / 60 / 24 / 7) + formatted = `${formatted}w` + } else if (modified >= 24 * 60 * 60 * 1000) { // 24h + formatted = Math.round(modified / 1000 / 60 / 60 / 24) + formatted = `${formatted}d` + } else if (modified >= 60 * 60 * 1000) { // 60min + formatted = Math.round(modified / 1000 / 60 / 60) + formatted = `${formatted}h` + } else if (modified >= 60 * 1000) { // 60s + formatted = Math.round(modified / 1000 / 60) + formatted = `${formatted}m` + } else { + formatted = Math.round(modified / 1000) + formatted = `${formatted}s` + } + + return
{formatted} ago
+ }, + }, + ] + + return columns + } \ No newline at end of file diff --git a/app/dashboard/files/page.tsx b/app/dashboard/files/page.tsx index 0353daa..eb34203 100644 --- a/app/dashboard/files/page.tsx +++ b/app/dashboard/files/page.tsx @@ -5,7 +5,7 @@ import { Button } from "@/components/ui/button" import Icon from '@mdi/react'; import { mdiDeleteOutline, mdiChevronUp, mdiFolderMoveOutline, mdiHomeOutline, mdiPencilOutline, mdiReload, mdiCloudDownloadOutline, mdiCloudUploadOutline, mdiContentCopy } from '@mdi/js'; -import { File, columns } from "@/app/dashboard/files/columns" +import { File, getColumns } from "@/app/dashboard/files/columns" import { DataTable } from "@/app/dashboard/files/data-table" async function getData(path: string): Promise { @@ -35,26 +35,57 @@ async function deleteFile(path: string) { return res } - - +async function renameFile(oldName: string, newName: string) { + await fetch(process.env.NEXT_PUBLIC_API_URL + '/api/rename/' + oldName + "/" + newName, { + method: 'POST', + headers: { + 'Authorization': 'Bearer ' + localStorage.getItem('token'), + }, + }) +} export default function Home() { const [data, setData] = useState([]) const [path, setPath] = useState("&.") const [selected, setSelected] = useState([]) + const [columns, setColumns] = useState([]) const refresh = async () => { const res = await getData(path) setData(res) } + const saveNewFileName = async (newName: string) => { + newName = newName.replaceAll("/", "&") + newName = newName.replaceAll(".", "&.") + newName = path + "&" + newName + newName = newName.replace("\n", "") + let oldName = selected[0].getValue("name") + oldName.replaceAll("/", "&") + oldName.replaceAll(".", "&.") + oldName = path + "&" + oldName + + renameFile(oldName, newName) + + setColumns(getColumns(-1, saveNewFileName)) + + setSelected([]) + + setTimeout(async () => { + const res = await getData(path) + setData(res) + }, 20) + } + useEffect(() => { + setColumns(getColumns(-1, saveNewFileName)) refresh() }, []) const goToHome = async () => { setPath("&.") const res = await getData("&.") + setColumns(getColumns(-1, saveNewFileName)) setData(res) } @@ -64,6 +95,7 @@ export default function Home() { const newPath = pathArr.join("&") setPath(newPath) const res = await getData(newPath) + setColumns(getColumns(-1, saveNewFileName)) setData(res) } @@ -78,6 +110,7 @@ export default function Home() { // go in the directory setPath(path + "&" + cell.getContext().row.original.name) const res = await getData(path + "&" + cell.getContext().row.original.name) + setColumns(getColumns(-1, saveNewFileName)) setData(res) } else { // open the file? or download it? @@ -111,6 +144,12 @@ export default function Home() { setSelected([]) refresh() } + + const renameSelected = async () => { + let row_index = selected[0].id + setColumns(getColumns(row_index, saveNewFileName)) + refresh() + } const addToFileUpload = async (e: any) => { e.preventDefault(); @@ -138,6 +177,7 @@ export default function Home() { e.stopPropagation(); e.preventDefault(); } + return ( @@ -166,7 +206,7 @@ export default function Home() { - + diff --git a/dash/src/files.rs b/dash/src/files.rs index 4eb4443..dfab2f0 100644 --- a/dash/src/files.rs +++ b/dash/src/files.rs @@ -36,6 +36,41 @@ async fn upload(MultipartForm(form): MultipartForm, path: web::Path< HttpResponse::Ok().body("Uploaded!") } +#[post("/rename/{pathname}/{new_name}")] +pub async fn rename(path: web::Path<(String, String)>) -> impl Responder { + let (mut pathname, mut new_name) = path.into_inner(); + + /*if pathname.contains("..") || pathname.contains("~") || pathname.starts_with("-") || pathname.contains("//") || pathname.contains("\\") || pathname.contains(":") || pathname.contains("*") || pathname.contains("?") || pathname.contains("\"") || pathname.contains("<") || pathname.contains(">") || pathname.contains("|") { + return Ok(HttpResponse::BadRequest().body("Invalid Pathname!")); + }*/ + // theoretically but since this only works with tokens the user can be trusted ... + + pathname = pathname.replace("&.", "."); + pathname = pathname.replace("&", "/"); + + new_name = new_name.replace("&.", "."); + new_name = new_name.replace("&", "/"); + + let path = Path::new(&pathname); + + if !path.exists() { + HttpResponse::BadRequest().body("File or Directory does not exist!"); + } else if pathname == "." || pathname == "/" { + return HttpResponse::BadRequest().body("Server Direcotry!"); + } + + let new_path = Path::new(&new_name); + + if new_path.exists() { + return HttpResponse::BadRequest().body("File or Directory already exists!"); + } + + std::fs::rename(path, new_path).unwrap(); + + HttpResponse::Ok().body(format!("Renamed {} to {}!", pathname, new_name)) +} + + #[derive(Serialize, Debug)] struct FileInfo { diff --git a/dash/src/main.rs b/dash/src/main.rs index 8acd68c..c366a71 100644 --- a/dash/src/main.rs +++ b/dash/src/main.rs @@ -259,8 +259,11 @@ async fn main() -> std::io::Result<()> { App::new() .wrap(cors) + + // websocket .route("/ws/", web::get().to(index)) + // api routes .service(scope("/api") .wrap(tokencheck::TokenCheck) .route("/is_running", web::post().to(is_running)) @@ -268,6 +271,7 @@ async fn main() -> std::io::Result<()> { .route("/execute_command", web::post().to(execute_command)) .service(files::get_files) .service(files::upload) + .service(files::rename) .service(files::delete_files) )}).bind(("127.0.0.1", port))?.run();