BUG: Having permission issues with the deleting and creating.

This commit is contained in:
Hayden Hargreaves 2025-03-26 23:21:16 -07:00
parent a2deb2252a
commit 4cd88c6505
3 changed files with 127 additions and 14 deletions

View File

@ -3,6 +3,7 @@ import {Healthcheck} from "./healthcheck";
import {printEndpoints, validateHash} from "./utils"; import {printEndpoints, validateHash} from "./utils";
import {LogRequestMiddleware} from "./log"; import {LogRequestMiddleware} from "./log";
import * as fs from "node:fs"; import * as fs from "node:fs";
import {mkdirSync, readFileSync, renameSync, rmSync, writeFileSync} from "node:fs";
import {entry} from "./entry"; import {entry} from "./entry";
import cors from "cors"; import cors from "cors";
import archiver from "archiver"; import archiver from "archiver";
@ -12,13 +13,14 @@ import {verifyToken} from "./authenicate";
import jwt from "jsonwebtoken"; import jwt from "jsonwebtoken";
import {config} from "dotenv"; import {config} from "dotenv";
import Multer from "multer"; import Multer from "multer";
import {mkdirSync, readFileSync, rmSync, writeFileSync} from "fs";
/** /**
* App details * App details
*/ */
const PORT = 5000; const PORT = 5000;
const APP: Express = express(); const APP: Express = express();
// TODO: BACK TO NORMAL PATH
// const ROOT: string = "/media/vault";
const ROOT: string = "/home/azpect"; const ROOT: string = "/home/azpect";
/** /**
@ -44,6 +46,16 @@ const corsOptions: cors.CorsOptions = {
}; };
APP.use(cors(corsOptions)); APP.use(cors(corsOptions));
/**
* File upload settings
*/
const upload = Multer({
dest: "tmp/",
limits: {
fileSize: 1024 * 1024 * 100
},
});
/** /**
* Apply middleware, this must be done before the routes are created. * Apply middleware, this must be done before the routes are created.
*/ */
@ -213,12 +225,6 @@ v1.post("/update", (req: Request, res: Response): void => {
} }
}); });
const upload = Multer({
dest: "tmp/",
limits: {
fileSize: 1024 * 1024 * 100
},
});
/** /**
* Custom type for the multer uploads. * Custom type for the multer uploads.
@ -252,7 +258,7 @@ v1.post("/upload", upload.array("files"), (req: Request, res: Response) => {
const newPath: string = path.join("/", ...cwd, file.originalname); const newPath: string = path.join("/", ...cwd, file.originalname);
// Write the new file // Write the new file
writeFileSync(newPath, data); writeFileSync(newPath, data, {mode: "666"});
// Delete the tmp file using a relative path // Delete the tmp file using a relative path
rmSync("./" + file.path); rmSync("./" + file.path);
@ -279,10 +285,9 @@ v1.post("/create", (req: Request, res: Response): void => {
try { try {
const newPath: string = path.join("/", ...cwd, name); const newPath: string = path.join("/", ...cwd, name);
if (name.endsWith("/")) { if (name.endsWith("/")) {
mkdirSync(newPath, {mode: "644"}) mkdirSync(newPath, {recursive: true, mode: "666"})
} else { } else {
console.log("NOT DIR"); writeFileSync(newPath, "", {mode: "666"})
writeFileSync(newPath, "", {mode: "644"})
} }
} catch (error: any) { } catch (error: any) {
if (error.code === 'EACCES') { if (error.code === 'EACCES') {
@ -303,6 +308,46 @@ v1.post("/create", (req: Request, res: Response): void => {
res.status(201).json({code: 201, message: "Success"}); res.status(201).json({code: 201, message: "Success"});
}); });
/**
* Files are not deleted from the file system, just moved to a hidden folder where
* they can be recovered if needed.
*
* The hidden folder will be called `.trash` stored in the root of the mount, and
* the files entire paths will be created.
*
* e.g., Deleting /media/vault/main.txt will get moved to
* `/media/vault/.trash/<timstamp>/media/vault/main.txt`
* assuming /media/vault is the mounted root.
* The timestamp will also be appended to allow files with the same path to be deleted.
*/
v1.post("/remove", (req: Request, res: Response): void => {
// Get the array of paths and the root
const {files, root} = req.body;
// Stores the name of the trash directory
const trashDir: string = ".trash";
const timestamp: string = (new Date()).toISOString();
console.log(timestamp);
for (const file of files) {
const oldPath = path.join("/", ...file);
const newPath = path.join("/", ...root, trashDir, timestamp, ...file);
console.log(oldPath, " -> ", newPath);
try {
mkdirSync(path.dirname(newPath), {recursive: true, mode: "666"});
renameSync(oldPath, newPath)
} catch (error) {
console.error(error);
res.status(500).json({code: 500, message: `Failed to delete. ${error}`})
return;
}
}
res.status(201).json({code: 201, message: `Deleted ${files.length} files.`});
});
/** /**
* Apply the routes to the server * Apply the routes to the server
*/ */

View File

@ -2,6 +2,7 @@
* Takes the user back to the home directory. The onClick prop * Takes the user back to the home directory. The onClick prop
* is called when the button is clicked. * is called when the button is clicked.
* @param onClick {function} * @param onClick {function}
* @param enabled
* @returns {JSX.Element} * @returns {JSX.Element}
* @constructor * @constructor
*/ */
@ -50,6 +51,21 @@ function CreateButton({onClick}) {
) )
} }
function DeleteButton({onClick, enabled}) {
return (
<button onClick={onClick}
disabled={!enabled}
title="Delete selected files/directories"
className="hover:bg-gray-200 p-1.5 mr-1 rounded-full transition-colors duration-150 disabled:text-gray-500 disabled:hover:bg-red-300 disabled:cursor-not-allowed text-black">
<svg className="h-5" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path
d="M18 6L17.1991 18.0129C17.129 19.065 17.0939 19.5911 16.8667 19.99C16.6666 20.3412 16.3648 20.6235 16.0011 20.7998C15.588 21 15.0607 21 14.0062 21H9.99377C8.93927 21 8.41202 21 7.99889 20.7998C7.63517 20.6235 7.33339 20.3412 7.13332 19.99C6.90607 19.5911 6.871 19.065 6.80086 18.0129L6 6M4 6H20M16 6L15.7294 5.18807C15.4671 4.40125 15.3359 4.00784 15.0927 3.71698C14.8779 3.46013 14.6021 3.26132 14.2905 3.13878C13.9376 3 13.523 3 12.6936 3H11.3064C10.477 3 10.0624 3 9.70951 3.13878C9.39792 3.26132 9.12208 3.46013 8.90729 3.71698C8.66405 4.00784 8.53292 4.40125 8.27064 5.18807L8 6"
stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round"/>
</svg>
</button>
);
}
/** /**
* *
* @param name {string} * @param name {string}
@ -73,10 +89,12 @@ function PathElement({name, index, onClick}) {
* @param backArrow {function} * @param backArrow {function}
* @param enabled {boolean} * @param enabled {boolean}
* @param create {function} * @param create {function}
* @param remove {function}
* @param removeEnable {boolean}
* @returns {JSX.Element} * @returns {JSX.Element}
* @constructor * @constructor
*/ */
export default function PathDisplay({path, updatePath, backHome, backArrow, enabled, create}) { export default function PathDisplay({path, updatePath, backHome, backArrow, enabled, create, remove, removeEnable}) {
return ( return (
<div <div
className="w-2/3 mt-8 border-b-1 border-gray-400 bg-white flex items-center truncate"> className="w-2/3 mt-8 border-b-1 border-gray-400 bg-white flex items-center truncate">
@ -84,6 +102,9 @@ export default function PathDisplay({path, updatePath, backHome, backArrow, enab
<BackButton onClick={backArrow} enabled={enabled}/> <BackButton onClick={backArrow} enabled={enabled}/>
{path.map((seg, idx) => <PathElement name={seg} key={idx} index={idx} onClick={updatePath}/>)} {path.map((seg, idx) => <PathElement name={seg} key={idx} index={idx} onClick={updatePath}/>)}
<div className="ml-auto h-full flex items-center"> <div className="ml-auto h-full flex items-center">
<DeleteButton onClick={remove} enabled={removeEnable}/>
</div>
<div className="h-full flex items-center">
<CreateButton onClick={create}/> <CreateButton onClick={create}/>
</div> </div>
</div> </div>

View File

@ -12,7 +12,9 @@ import CreateDirectory from "../components/CreateDirectory.jsx";
export default function Dashboard() { export default function Dashboard() {
// Store the default path // Store the default path
const defaultPath = ["media", "vault"]; // TODO: BACK TO NORMAL PATH
// const defaultPath = ["media", "vault"];
const defaultPath = ["home", "azpect"];
/** /**
* URL To the backend web server. * URL To the backend web server.
@ -387,6 +389,49 @@ export default function Dashboard() {
}); });
}; };
/**
* Remove the selected files.
*/
const removeSelected = () => {
if (selected.length === 0) {
return setError("Please select files or directories to delete");
}
const remove = async (files) => {
const resp = await fetch(`${backendUrl}/v1/remove`, {
method: "POST",
headers: {
"Content-Type": "application/json",
"authorization": `Bearer ${token}`,
},
body: JSON.stringify({files, root: defaultPath})
});
if (!resp.ok) {
const data = await resp.json()
setError(data.error);
return data;
}
return await resp.json();
};
// Files are stored as arrays of paths
const files = [];
for (const file of selected) {
files.push([...path, file]);
}
remove(files).then((data) => {
if (data.code === 201) {
console.log(data);
setSelected([]);
}
}).catch((error) => {
setError(error);
});
};
return ( return (
<div className="w-full min-h-screen h-screen pb-8"> <div className="w-full min-h-screen h-screen pb-8">
<Navbar downloadFiles={downloadFiles} uploadFiles={toggleUploading}/> <Navbar downloadFiles={downloadFiles} uploadFiles={toggleUploading}/>
@ -401,10 +446,12 @@ export default function Dashboard() {
loading={contentLoading}/>} loading={contentLoading}/>}
<PathDisplay path={path} updatePath={updatePath} backHome={backHome} backArrow={backArrow} <PathDisplay path={path} updatePath={updatePath} backHome={backHome} backArrow={backArrow}
enabled={path.length > defaultPath.length} create={createDir}/> enabled={path.length > defaultPath.length} create={createDir} remove={removeSelected}
removeEnable={selected.length > 0}/>
<div className="w-2/3 h-5/6 overflow-y-auto border-1 border-gray-300"> <div className="w-2/3 h-5/6 overflow-y-auto border-1 border-gray-300">
{childrenLoading && <ChildrenLoading/>} {childrenLoading && <ChildrenLoading/>}
<DirectoryList dirs={files} showHidden={showHidden} appendPath={appendPath} <DirectoryList dirs={files} showHidden={showHidden} appendPath={appendPath}
// TODO: Rework the toggleSelected functionality
toggleSelected={toggleSelected} toggleEditing={toggleEditing}/> toggleSelected={toggleSelected} toggleEditing={toggleEditing}/>
</div> </div>
<div className="w-2/3 flex justify-end items-center"> <div className="w-2/3 flex justify-end items-center">