From fba2d3da7206563b8560d1e9496b4995675174b9 Mon Sep 17 00:00:00 2001 From: Mark Hughes Date: Fri, 21 Feb 2025 17:53:25 +1100 Subject: [PATCH] fix: removing filetree callback hell --- packages/server/lib/commands/clipboard.js | 12 +- packages/server/lib/commands/createFile.js | 10 +- packages/server/lib/commands/createFiles.js | 22 +- packages/server/lib/commands/createFolder.js | 10 +- packages/server/lib/commands/createFolders.js | 20 +- packages/server/lib/commands/deleteFile.js | 9 +- packages/server/lib/commands/rename.js | 9 +- packages/server/lib/commands/saveFile.js | 16 +- packages/server/lib/services/filetree.js | 364 +++++++++++------- packages/server/lib/services/server.js | 240 +++++++----- packages/server/lib/services/utils.js | 4 +- 11 files changed, 435 insertions(+), 281 deletions(-) diff --git a/packages/server/lib/commands/clipboard.js b/packages/server/lib/commands/clipboard.js index d2a1ccd8..8f217b6a 100644 --- a/packages/server/lib/commands/clipboard.js +++ b/packages/server/lib/commands/clipboard.js @@ -10,9 +10,17 @@ export default { const src = msg.data.src; const dst = msg.data.dst; const type = msg.data.type; + log.info(ws, null, `Clipboard ${type}: ${src} -> ${dst}`); - if (config.readOnly) return sendError(sid, vId, "Files are read-only"); - if (!validatePaths([src, dst], msg.type, ws, sid, vId)) return; + + if (config.readOnly) { + return sendError(sid, vId, "Files are read-only"); + } + + if (!validatePaths([src, dst], msg.type, ws, sid, vId)) { + return; + } + if (new RegExp(`^${escRe(msg.data.src)}/`).test(msg.data.dst)) { return sendError(sid, vId, "Can't copy directory into itself"); } diff --git a/packages/server/lib/commands/createFile.js b/packages/server/lib/commands/createFile.js index 810c20f4..fb6e1928 100644 --- a/packages/server/lib/commands/createFile.js +++ b/packages/server/lib/commands/createFile.js @@ -8,8 +8,12 @@ export default { if (!validatePaths(msg.data, msg.type, ws, sid, vId)) { return; } - filetree.mk(msg.data, (err) => { - if (err) sendError(sid, vId, `Error creating file: ${err.message}`); - }); + + try { + await filetree.mk(msg.data); + } catch (err) { + log.error(ws, null, err); + sendError(sid, vId, `Error creating file: ${err.message}`); + } }, }; diff --git a/packages/server/lib/commands/createFiles.js b/packages/server/lib/commands/createFiles.js index 188d7f36..6975ae77 100644 --- a/packages/server/lib/commands/createFiles.js +++ b/packages/server/lib/commands/createFiles.js @@ -13,18 +13,14 @@ export default { return; } - await Promise.all( - msg.data.files.map((file) => { - return new Promise((resolve) => { - filetree.mkdir(utils.addFilesPath(path.dirname(file)), (err) => { - if (err) log.error(ws, null, err); - filetree.mk(utils.addFilesPath(file), (err) => { - if (err) log.error(ws, null, err); - resolve(); - }); - }); - }); - }) - ); + for (const file of msg.data.files) { + try { + await filetree.mkdir(utils.addFilesPath(path.dirname(file))); + await filetree.mk(utils.addFilesPath(file)); + } catch (err) { + log.error(ws, null, err); + sendError(sid, vId, `Error creating file`); + } + } }, }; diff --git a/packages/server/lib/commands/createFolder.js b/packages/server/lib/commands/createFolder.js index 7d430c36..0e0b76dc 100644 --- a/packages/server/lib/commands/createFolder.js +++ b/packages/server/lib/commands/createFolder.js @@ -1,4 +1,5 @@ import filetree from "../services/filetree.js"; +import log from "../services/log.js"; export default { handler: async ({ validatePaths, sid, config, msg, ws, vId, sendError }) => { @@ -9,8 +10,11 @@ export default { return; } - filetree.mkdir(msg.data, (err) => { - if (err) sendError(sid, vId, `Error creating folder: ${err.message}`); - }); + try { + await filetree.mkdir(msg.data); + } catch (err) { + log.error(ws, null, err); + sendError(sid, vId, `Error creating folder: ${err.message}`); + } }, }; diff --git a/packages/server/lib/commands/createFolders.js b/packages/server/lib/commands/createFolders.js index a926b8e8..21a42d99 100644 --- a/packages/server/lib/commands/createFolders.js +++ b/packages/server/lib/commands/createFolders.js @@ -12,17 +12,13 @@ export default { return; } - await Promise.all( - msg.data.folders.map((folder) => { - return new Promise((resolve) => { - filetree.mkdir(utils.addFilesPath(folder), (err) => { - if (err) { - log.error(ws, null, err); - } - resolve(); - }); - }); - }) - ); + for (const folder of msg.data.folders) { + try { + await filetree.mkdir(utils.addFilesPath(folder)); + } catch (err) { + log.error(ws, null, err); + sendError(sid, vId, `Error creating folder ${folder}`); + } + } }, }; diff --git a/packages/server/lib/commands/deleteFile.js b/packages/server/lib/commands/deleteFile.js index 310ddf5b..39b381d4 100644 --- a/packages/server/lib/commands/deleteFile.js +++ b/packages/server/lib/commands/deleteFile.js @@ -12,6 +12,13 @@ export default { return; } log.info(ws, null, `Deleting: ${msg.data}`); - filetree.del(msg.data); + + try { + await filetree.del(msg.data); + } catch (err) { + log.info(ws, null, `Error deleting file: ${msg.data}`); + log.error(err); + return sendError(sid, vId, "Error deleting file"); + } }, }; diff --git a/packages/server/lib/commands/rename.js b/packages/server/lib/commands/rename.js index 2e2dd74e..1edb6ca7 100644 --- a/packages/server/lib/commands/rename.js +++ b/packages/server/lib/commands/rename.js @@ -23,7 +23,14 @@ export default { sendError(sid, vId, "Invalid rename request"); return; } - filetree.move(rSrc, rDst); + + try { + await filetree.move(rSrc, rDst); + } catch (err) { + log.error(ws, null, err); + sendError(sid, vId, `Error renaming ${rSrc} to ${rDst}`); + return; + } // update sharelinks to new destination const links = db.get("links"); diff --git a/packages/server/lib/commands/saveFile.js b/packages/server/lib/commands/saveFile.js index 0b18653c..f56fdf52 100644 --- a/packages/server/lib/commands/saveFile.js +++ b/packages/server/lib/commands/saveFile.js @@ -23,11 +23,15 @@ export default { log.info(ws, null, `Saving: ${msg.data.to}`); - filetree.save(msg.data.to, msg.data.value, (err) => { - if (err) { - sendError(sid, vId, `Error saving: ${err.message}`); - log.error(err); - } else sendObj(sid, { type: "SAVE_STATUS", vId, status: err ? 1 : 0 }); - }); + try { + await filetree.save(msg.data.to, msg.data.value); + } catch (err) { + sendObj(sid, { type: "SAVE_STATUS", vId, status: 1 }); + sendError(sid, vId, `Error saving: ${err.message}`); + log.error(err); + return; + } + + sendObj(sid, { type: "SAVE_STATUS", vId, status: 0 }); }, }; diff --git a/packages/server/lib/services/filetree.js b/packages/server/lib/services/filetree.js index b667a184..fde732e5 100644 --- a/packages/server/lib/services/filetree.js +++ b/packages/server/lib/services/filetree.js @@ -3,11 +3,11 @@ import debounce from "lodash.debounce"; import chokidar from "chokidar"; import escRe from "escape-string-regexp"; -import fs from "fs"; +import fs from "fs/promises"; + import path from "path"; import rrdir from "rrdir"; import rfdc from "rfdc"; -import util from "util"; import log from "./log.js"; import paths from "./paths.js"; @@ -16,7 +16,6 @@ import utils from "./utils.js"; import { EventEmitter } from "events"; const clone = rfdc(); -const lstat = util.promisify(fs.lstat); let dirs = {}; let todoDirs = []; @@ -67,7 +66,7 @@ class DroppyFileTree extends EventEmitter { let stats; try { - stats = await lstat(fullDir); + stats = await fs.lstat(fullDir); } catch (err) { log.error(err); } @@ -114,179 +113,266 @@ class DroppyFileTree extends EventEmitter { this.updateDirInCache(dir, stats, readDirs, readFiles); } - del(dir) { - fs.stat(utils.addFilesPath(dir), (err, stats) => { - if (err) { - log.error(err); - } - if (!stats) { - return; - } + async del(dir) { + /** + * @type {fs.Stats} + */ + let stats; + try { + stats = await fs.stat(utils.addFilesPath(dir)); + } catch (err) { + log.error(err); - if (stats.isFile()) { - this.unlink(dir); - } else if (stats.isDirectory()) { - this.unlinkdir(dir); - } - }); + throw err; + } + + if (stats.isFile()) { + await this.unlink(dir); + } else if (stats.isDirectory()) { + await this.unlinkdir(dir); + } else { + throw new Error("Invalid file type"); + } } unlink(dir) { this.lookAway(); - utils.rm(utils.addFilesPath(dir), (err) => { - if (err) log.error(err); - delete dirs[path.dirname(dir)].files[path.basename(dir)]; - this.update(path.dirname(dir)); + + // TODO: remove new promise, change to async when utils.rm is async + return new Promise((resolve, reject) => { + utils.rm(utils.addFilesPath(dir), (err) => { + if (err) { + log.error(err); + reject(err); + } + + delete dirs[path.dirname(dir)].files[path.basename(dir)]; + this.update(path.dirname(dir)); + resolve(); + }); }); } unlinkdir(dir) { this.lookAway(); - utils.rmdir(utils.addFilesPath(dir), (err) => { - if (err) log.error(err); - delete dirs[dir]; - Object.keys(dirs).forEach((d) => { - if (new RegExp(`^${escRe(dir)}/`).test(d)) delete dirs[d]; + + // TODO: remove new promise, change to async when utils.rmdir is async + return new Promise((resolve, reject) => { + utils.rmdir(utils.addFilesPath(dir), (err) => { + if (err) { + log.error(err); + reject(err); + } + + delete dirs[dir]; + Object.keys(dirs).forEach((d) => { + if (new RegExp(`^${escRe(dir)}/`).test(d)) { + delete dirs[d]; + } + }); + + this.update(path.dirname(dir)); + resolve(); }); - this.update(path.dirname(dir)); }); } - clipboard(src, dst, type) { - fs.stat(utils.addFilesPath(src), (err, stats) => { - this.lookAway(); - if (err) log.error(err); - if (stats.isFile()) { - this[type === "cut" ? "mv" : "cp"](src, dst); - } else if (stats.isDirectory()) { - this[type === "cut" ? "mvdir" : "cpdir"](src, dst); + async clipboard(src, dst, type) { + /** + * @type {fs.Stats} + */ + let stats; + + try { + stats = await fs.stat(utils.addFilesPath(src)); + } catch (err) { + log.error(err); + + throw err; + } + + this.lookAway(); + + /** + * @type {Function} + */ + let callable; + + if (stats.isFile()) { + if (type === "cut") { + await this.mv(src, dst); + } else { + await this.cp(src, dst); } - }); + } else if (stats.isDirectory()) { + if (type === "cut") { + await this.mvdir(src, dst); + } else { + await this.cpdir(src, dst); + } + } + + return callable(src, dst); } - mk(dir, cb) { + async mk(dir) { this.lookAway(); - fs.stat(utils.addFilesPath(dir), (err) => { + + try { + await fs.stat(utils.addFilesPath(dir)); + } catch (err) { if (err && err.code === "ENOENT") { - fs.open(utils.addFilesPath(dir), "wx", (err, fd) => { - if (err) { - log.error(err); - if (cb) cb(err); - return; - } - fs.close(fd, (error) => { - if (error) log.error(error); - dirs[path.dirname(dir)].files[path.basename(dir)] = { - size: 0, - mtime: Date.now(), - }; - this.update(path.dirname(dir)); - if (cb) cb(); - }); - }); - } else if (err) { - log.error(err); - if (cb) cb(err); + const fd = await fs.open(utils.addFilesPath(dir), "wx"); + + await fs.close(fd); + + dirs[path.dirname(dir)].files[path.basename(dir)] = { + size: 0, + mtime: Date.now(), + }; + + this.update(path.dirname(dir)); } else { - if (cb) cb(); + log.error(err); + + throw err; } - }); + } } - mkdir(dir, cb) { + async mkdir(dir) { this.lookAway(); - fs.stat(utils.addFilesPath(dir), (err) => { - if (err && err.code === "ENOENT") { - utils.mkdir(utils.addFilesPath(dir), (err) => { - if (err) { - log.error(err); - if (cb) cb(err); - return; - } - dirs[dir] = { files: {}, size: 0, mtime: Date.now() }; - this.update(path.dirname(dir)); - if (cb) cb(); - }); - } else if (err) { + + try { + await fs.stat(utils.addFilesPath(dir)); + } catch (err) { + if (err?.code !== "ENOENT") { log.error(err); - if (cb) cb(err); - } else { - if (cb) cb(); + throw err; } - }); + + await utils.mkdir(utils.addFilesPath(dir)); + + dirs[dir] = { files: {}, size: 0, mtime: Date.now() }; + this.update(path.dirname(dir)); + } } - move(src, dst, cb) { + async move(src, dst) { this.lookAway(); - fs.stat(utils.addFilesPath(src), (err, stats) => { - if (err) log.error(err); + + try { + const stats = await fs.stat(utils.addFilesPath(src)); + if (stats.isFile()) { - this.mv(src, dst, cb); + await this.mv(src, dst); } else if (stats.isDirectory()) { - this.mvdir(src, dst, cb); + await this.mvdir(src, dst); } - }); + } catch (err) { + log.error(err); + + throw err; + } } - mv(src, dst, cb) { + mv(src, dst) { this.lookAway(); - utils.move(utils.addFilesPath(src), utils.addFilesPath(dst), (err) => { - if (err) log.error(err); - dirs[path.dirname(dst)].files[path.basename(dst)] = - dirs[path.dirname(src)].files[path.basename(src)]; - delete dirs[path.dirname(src)].files[path.basename(src)]; - this.update(path.dirname(src)); - this.update(path.dirname(dst)); - if (cb) cb(); + + return new Promise((resolve, reject) => { + // TODO: asjust to async/await when utils.move is async + utils.move(utils.addFilesPath(src), utils.addFilesPath(dst), (err) => { + if (err) { + log.error(err); + + reject(err); + } + + dirs[path.dirname(dst)].files[path.basename(dst)] = + dirs[path.dirname(src)].files[path.basename(src)]; + + delete dirs[path.dirname(src)].files[path.basename(src)]; + + this.update(path.dirname(src)); + this.update(path.dirname(dst)); + + resolve(); + }); }); } - mvdir(src, dst, cb) { + mvdir(src, dst) { this.lookAway(); - utils.move(utils.addFilesPath(src), utils.addFilesPath(dst), (err) => { - if (err) log.error(err); - // Basedir - dirs[dst] = dirs[src]; - delete dirs[src]; - // Subdirs - Object.keys(dirs).forEach((dir) => { - if ( - new RegExp(`^${escRe(src)}/`).test(dir) && - dir !== src && - dir !== dst - ) { - dirs[dir.replace(new RegExp(`^${escRe(src)}/`), `${dst}/`)] = - dirs[dir]; - delete dirs[dir]; + + return new Promise((resolve, reject) => { + // TODO: asjust to async/await when utils.move is async + utils.move(utils.addFilesPath(src), utils.addFilesPath(dst), (err) => { + if (err) { + log.error(err); + + reject(err); } + + dirs[dst] = dirs[src]; + delete dirs[src]; + + Object.keys(dirs).forEach((dir) => { + if ( + new RegExp(`^${escRe(src)}/`).test(dir) && + dir !== src && + dir !== dst + ) { + dirs[dir.replace(new RegExp(`^${escRe(src)}/`), `${dst}/`)] = + dirs[dir]; + delete dirs[dir]; + } + }); + + this.update(path.dirname(src)); + this.update(path.dirname(dst)); + + resolve(); }); - this.update(path.dirname(src)); - this.update(path.dirname(dst)); - if (cb) cb(); }); } - cp(src, dst, cb) { + cp(src, dst) { this.lookAway(); - utils.copyFile(utils.addFilesPath(src), utils.addFilesPath(dst), () => { - dirs[path.dirname(dst)].files[path.basename(dst)] = clone( - dirs[path.dirname(src)].files[path.basename(src)] + + return new Promise((resolve, reject) => { + utils.copyFile( + utils.addFilesPath(src), + utils.addFilesPath(dst), + (err) => { + if (err) { + log.error(err); + + reject(err); + return; + } + + dirs[path.dirname(dst)].files[path.basename(dst)] = clone( + dirs[path.dirname(src)].files[path.basename(src)] + ); + + dirs[path.dirname(dst)].files[path.basename(dst)].mtime = Date.now(); + + this.update(path.dirname(dst)); + + resolve(); + } ); - dirs[path.dirname(dst)].files[path.basename(dst)].mtime = Date.now(); - this.update(path.dirname(dst)); - if (cb) { - cb(); - } }); } - async cpdir(src, dst, cb) { + async cpdir(src, dst) { this.lookAway(); await utils.copyDir(utils.addFilesPath(src), utils.addFilesPath(dst)); // Basedir dirs[dst] = clone(dirs[src]); dirs[dst].mtime = Date.now(); + // Subdirs Object.keys(dirs).forEach((dir) => { if ( @@ -301,23 +387,29 @@ class DroppyFileTree extends EventEmitter { Date.now(); } }); + this.update(path.dirname(dst)); - if (cb) cb(); } - save(dst, data, cb) { + async save(dst, data) { this.lookAway(); - fs.stat(utils.addFilesPath(dst), (err) => { - if (err && err.code !== "ENOENT") return cb(err); - fs.writeFile(utils.addFilesPath(dst), data, (err) => { - dirs[path.dirname(dst)].files[path.basename(dst)] = { - size: Buffer.byteLength(data), - mtime: Date.now(), - }; - this.update(path.dirname(dst)); - if (cb) cb(err); - }); - }); + try { + await fs.stat(utils.addFilesPath(dst)); + } catch (err) { + if (err.code !== "ENOENT") { + log.error(err); + throw err; + } + } + + await fs.writeFile(utils.addFilesPath(dst), data); + + dirs[path.dirname(dst)].files[path.basename(dst)] = { + size: Buffer.byteLength(data), + mtime: Date.now(), + }; + + this.update(path.dirname(dst)); } search(query, p) { @@ -416,6 +508,7 @@ class DroppyFileTree extends EventEmitter { const readDirObj = {}, readDirKeys = []; + readDirs .sort((a, b) => utils.naturalSort(a.path, b.path)) .forEach((d) => { @@ -499,6 +592,7 @@ class DroppyFileTree extends EventEmitter { : path.basename(file); entries[name] = ["f", mtime, f.size].join("|"); }); + folders.forEach((folder) => { if (dirs[folder]) { const d = dirs[folder]; diff --git a/packages/server/lib/services/server.js b/packages/server/lib/services/server.js index 64e9c32d..52304176 100644 --- a/packages/server/lib/services/server.js +++ b/packages/server/lib/services/server.js @@ -1,10 +1,10 @@ "use strict"; -import fs from "fs"; +import fs from "fs/promises"; +import { createWriteStream } from 'fs'; import os from "os"; import path from "path"; import { promisify } from "util"; -import { readFile } from "fs/promises"; import throttle from "lodash.throttle"; import busboy from "busboy"; @@ -66,17 +66,11 @@ export default async function droppy(opts, isStandalone, dev, callback) { setupProcess(isStandalone); try { - await promisify((cb) => { - utils.mkdir([paths.get().files, paths.get().config], cb); - })(); + await utils.mkdir([paths.get().files, paths.get().config]); - await promisify((cb) => { - if (isStandalone) { - fs.writeFile(paths.get().pid, String(process.pid), cb); - } else { - cb(); - } - })(); + if (isStandalone) { + await fs.writeFile(paths.get().pid, String(process.pid)); + } config = await cfg.init(opts); if (dev) { @@ -119,15 +113,15 @@ export default async function droppy(opts, isStandalone, dev, callback) { } else cb(); })(); - await promisify((cb) => { - log.info("Caching files ..."); - filetree.init(config); - filetree.updateDir(null).then(() => { - if (config.watch) filetree.watch(); - log.info("Caching files done"); - cb(); - }); - })(); + log.info("Caching files ..."); + filetree.init(config); + await filetree.updateDir(null); + if (config.watch) { + filetree.watch(); + } + + log.info("Caching files done"); + await promisify((cb) => { if (typeof config.keepAlive === "number" && config.keepAlive > 0) { @@ -234,7 +228,7 @@ async function startListeners(callback) { } try { - fs.unlinkSync(socket); + fs.unlink(socket); } catch (err) { if (err.code !== "ENOENT") { return callback( @@ -282,7 +276,7 @@ async function startListeners(callback) { return resolve(); } - server.on("listening", () => { + server.on("listening", async () => { server.removeAllListeners("error"); listenerCount++; setupWebSocket(server); @@ -290,7 +284,7 @@ async function startListeners(callback) { if (target.socket) { // socket - fs.chmodSync(target.socket, 0o666); // make it rw + await fs.chmod(target.socket, 0o666); // make it rw // a unix socket URL should normally percent-encode the path, but // we're printing a path-less URL so pretty-print it with slashes. log.info( @@ -639,7 +633,7 @@ function send(ws, data) { })(ws, data, 0); } -function handleGETandHEAD(req, res) { +async function handleGETandHEAD(req, res) { const URI = decodeURIComponent(req.url); if (config.public && !cookies.get(req.headers.cookie)) { @@ -703,16 +697,26 @@ function handleGETandHEAD(req, res) { handleFileRequest(req, res, false); } else if (/^\/!\/zip\/[\s\S]+/.test(URI)) { const zipPath = utils.addFilesPath(URI.substring(6)); - fs.stat(zipPath, (err, stats) => { - if (!err && stats.isDirectory()) { - streamArchive(req, res, zipPath, true, stats, false); - } else { - if (err) log.error(err); - res.statusCode = 404; - res.end(); - log.info(req, res); - } - }); + + /** + * @type {import("fs").Stats} + */ + let stats = null; + + try { + stats = await fs.stat(zipPath); + } catch (err) { + log.error(err); + } + + if (stats && stats.isDirectory()) { + streamArchive(req, res, zipPath, true, stats, false); + } else { + res.statusCode = 404; + res.end(); + log.info(req, res); + } + } else { redirectToRoot(req, res); } @@ -946,7 +950,7 @@ function handleResourceRequest(req, res, resourceName) { log.info(req, res); } -function handleFileRequest(req, res, download) { +async function handleFileRequest(req, res, download) { const URI = decodeURIComponent(req.url); let shareLink, filepath; @@ -971,26 +975,28 @@ function handleFileRequest(req, res, download) { filepath = utils.addFilesPath(`/${[parts[2]]}`); } - fs.stat(filepath, (error, stats) => { - if (!error && stats) { - if (stats.isDirectory() && shareLink) { - streamArchive(req, res, filepath, download, stats, shareLink); - } else { - streamFile(req, res, filepath, download, stats, shareLink); - } + try { + const stats = await fs.stat(filepath); + + if (stats.isDirectory() && shareLink) { + streamArchive(req, res, filepath, download, stats, shareLink); } else { - if (error.code === "ENOENT") { - res.statusCode = 404; - } else if (error.code === "EACCES") { - res.statusCode = 403; - } else { - res.statusCode = 500; - } - log.error(error); - res.end(); + streamFile(req, res, filepath, download, stats, shareLink); } - log.info(req, res); - }); + + } catch (err) { + if (err.code === "ENOENT") { + res.statusCode = 404; + } else if (err.code === "EACCES") { + res.statusCode = 403; + } else { + res.statusCode = 500; + } + log.error(err); + res.end(); + } + + log.info(req, res); } async function handleTypeRequest(req, res, file) { @@ -1097,28 +1103,31 @@ function handleUploadRequest(req, res) { const dst = utils.addFilesPath(path.join(dstDir, tmpPath)); - utils.mkdir(path.dirname(dst), () => { - fs.stat(dst, (err) => { + utils.mkdir(path.dirname(dst)).then(async () => { + try { + + await fs.stat(dst); + + if (req.query.rename === "1") { + utils.getNewPath(dst, (newDst) => { + const ws = createWriteStream(newDst, { mode: "644" }); + ws.on("error", onWriteError); + file.pipe(ws); + }); + } else { + const ws = createWriteStream(dst, { mode: "644" }); + ws.on("error", onWriteError); + file.pipe(ws); + } + } catch (err) { if (err && err.code === "ENOENT") { - const ws = fs.createWriteStream(dst, { mode: "644" }); + const ws = createWriteStream(dst, { mode: "644" }); ws.on("error", onWriteError); file.pipe(ws); - } else if (!err) { - if (req.query.rename === "1") { - utils.getNewPath(dst, (newDst) => { - const ws = fs.createWriteStream(newDst, { mode: "644" }); - ws.on("error", onWriteError); - file.pipe(ws); - }); - } else { - const ws = fs.createWriteStream(dst, { mode: "644" }); - ws.on("error", onWriteError); - file.pipe(ws); - } } else { onWriteError(err); } - }); + } }); }); @@ -1264,35 +1273,47 @@ function cleanupLinks(callback) { if (Object.keys(links).length === 0) { callback(); } else { - Object.keys(links).forEach((link) => { + Object.keys(links).forEach(async (link) => { linkcount++; - (function (shareLink, location) { - // check for links not matching the configured length - if (shareLink.length !== config.linkLength) { - log.debug( - `deleting link not matching the configured length: ${shareLink}` - ); - delete links[shareLink]; - if (++cbcount === linkcount) { - db.set("links", links); - callback(); - } - return; + + const shareLink = link; + const location = links[link].location; + + // check for links not matching the configured length + if (shareLink.length !== config.linkLength) { + log.debug( + `deleting link not matching the configured length: ${shareLink}` + ); + delete links[shareLink]; + if (++cbcount === linkcount) { + db.set("links", links); + callback(); } - // check for links where the target does not exist anymore - fs.stat(path.join(paths.get().files, location), (error, stats) => { - if (!stats || error) { - log.debug(`deleting nonexistant link: ${shareLink}`); - delete links[shareLink]; - } - if (++cbcount === linkcount) { - if (JSON.stringify(links) !== JSON.stringify(db.get("links"))) { - db.set("links", links); - } - callback(); - } - }); - })(link, links[link].location); + return; + } + // check for links where the target does not exist anymore + + /** + * @type {import("fs").Stats} + */ + let stats; + try { + stats = await fs.stat(path.join(paths.get().files, location)); + } catch { + // Ignore error + } + + if (!stats) { + log.debug(`deleting nonexistant link: ${shareLink}`); + delete links[shareLink]; + } + + if (++cbcount === linkcount) { + if (JSON.stringify(links) !== JSON.stringify(db.get("links"))) { + db.set("links", links); + } + callback(); + } }); } } @@ -1439,11 +1460,11 @@ async function tlsSetup(opts, cb) { return cb(new Error("Missing TLS option 'cert'")); } - const cert = await readFile( + const cert = await fs.readFile( path.resolve(paths.get().config, ut(opts.cert)), "utf8" ); - const key = await readFile( + const key = await fs.readFile( path.resolve(paths.get().config, ut(opts.key)), "utf8" ); @@ -1476,11 +1497,15 @@ function setupProcess(standalone) { process.on("SIGTERM", endProcess.bind(null, "SIGTERM")); process.on("unhandledRejection", (error) => { log.error(error); - if (dieOnError) process.exit(1); + if (dieOnError) { + process.exit(1); + } }); process.on("uncaughtException", (error) => { log.error(error); - if (dieOnError) process.exit(1); + if (dieOnError) { + process.exit(1); + } }); } } @@ -1490,15 +1515,22 @@ function endProcess(signal) { let count = 0; log.info(`Received ${red(signal)} - Shutting down ...`); Object.keys(clients).forEach((sid) => { - if (!clients[sid] || !clients[sid].ws) return; + if (!clients[sid] || !clients[sid].ws) { + return; + } + if (clients[sid].ws.readyState < 2) { count++; clients[sid].ws.close(1001); } }); - if (count > 0) log.info(`Closed ${count} WebSocket${count > 1 ? "s" : ""}`); + + if (count > 0) { + log.info(`Closed ${count} WebSocket${count > 1 ? "s" : ""}`); + } + try { - fs.unlinkSync(paths.get().pid); + fs.unlink(paths.get().pid); } catch { // Fail silently } diff --git a/packages/server/lib/services/utils.js b/packages/server/lib/services/utils.js index c4530f51..09337ed2 100644 --- a/packages/server/lib/services/utils.js +++ b/packages/server/lib/services/utils.js @@ -55,7 +55,9 @@ class DroppyUtils { for (const d of Array.isArray(dir) ? dir : [dir]) { await mkdir(d, { mode: "755", recursive: true }); } - cb(); + if (cb) { + cb(); + } } rm(p, cb) {