@muhgholy/next-drive 4.23.1 → 4.23.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -521,6 +521,20 @@ const stats = await DatabaseMongoDrive.aggregate([
521
521
  ]);
522
522
  ```
523
523
 
524
+ ### Cleanup Orphaned Files
525
+
526
+ Remove orphaned file folders from storage that no longer exist in the database, and clean up leftover temp directories:
527
+
528
+ ```typescript
529
+ import { driveCleanup } from "@muhgholy/next-drive/server";
530
+
531
+ const result = await driveCleanup();
532
+ console.log(`Removed ${result.removed.length} orphaned folders`);
533
+ console.log(`Freed ${result.totalFreedInBytes} bytes`);
534
+ ```
535
+
536
+ **Returns:** `{ removed: string[], totalFreedInBytes: number }`
537
+
524
538
  ### Delete File or Folder
525
539
 
526
540
  Permanently delete a file or folder from the drive system:
@@ -1,7 +1,7 @@
1
1
  import formidable from 'formidable';
2
2
  import path from 'path';
3
3
  import fs from 'fs';
4
- import os3 from 'os';
4
+ import os2 from 'os';
5
5
  import crypto2 from 'crypto';
6
6
  import mongoose, { Schema, isValidObjectId } from 'mongoose';
7
7
  import sharp2 from 'sharp';
@@ -260,7 +260,7 @@ var driveConfiguration = async (config) => {
260
260
  if (g.migrationPromise) await g.migrationPromise;
261
261
  return g.config;
262
262
  }
263
- const resolvedPath = config.storage?.path || path.join(os3.tmpdir(), "next-drive-data");
263
+ const resolvedPath = config.storage?.path || path.join(os2.tmpdir(), "next-drive-data");
264
264
  const mode = config.mode || "NORMAL";
265
265
  if (mode === "ROOT") {
266
266
  g.config = {
@@ -1594,7 +1594,7 @@ var driveUpload = async (source, key, options) => {
1594
1594
  const stats = fs.statSync(source);
1595
1595
  fileSize = stats.size;
1596
1596
  } else if (Buffer.isBuffer(source)) {
1597
- const tempDir = path.join(os3.tmpdir(), "next-drive-uploads");
1597
+ const tempDir = path.join(os2.tmpdir(), "next-drive-uploads");
1598
1598
  if (!fs.existsSync(tempDir)) {
1599
1599
  fs.mkdirSync(tempDir, { recursive: true });
1600
1600
  }
@@ -1603,7 +1603,7 @@ var driveUpload = async (source, key, options) => {
1603
1603
  sourceFilePath = tempFilePath;
1604
1604
  fileSize = source.length;
1605
1605
  } else {
1606
- const tempDir = path.join(os3.tmpdir(), "next-drive-uploads");
1606
+ const tempDir = path.join(os2.tmpdir(), "next-drive-uploads");
1607
1607
  if (!fs.existsSync(tempDir)) {
1608
1608
  fs.mkdirSync(tempDir, { recursive: true });
1609
1609
  }
@@ -1703,6 +1703,81 @@ var driveUpload = async (source, key, options) => {
1703
1703
  }
1704
1704
  }
1705
1705
  };
1706
+ var driveCleanup = async () => {
1707
+ const config = getDriveConfig();
1708
+ const fileDir = path.join(config.storage.path, "file");
1709
+ if (!fs.existsSync(fileDir)) {
1710
+ return { removed: [], totalFreedInBytes: 0 };
1711
+ }
1712
+ const folderNames = fs.readdirSync(fileDir).filter((name) => {
1713
+ const fullPath = path.join(fileDir, name);
1714
+ return fs.statSync(fullPath).isDirectory();
1715
+ });
1716
+ if (folderNames.length === 0) {
1717
+ return { removed: [], totalFreedInBytes: 0 };
1718
+ }
1719
+ const getDirSize = (dirPath) => {
1720
+ let size = 0;
1721
+ try {
1722
+ const entries = fs.readdirSync(dirPath, { withFileTypes: true });
1723
+ for (const entry of entries) {
1724
+ const entryPath = path.join(dirPath, entry.name);
1725
+ if (entry.isFile()) {
1726
+ size += fs.statSync(entryPath).size;
1727
+ } else if (entry.isDirectory()) {
1728
+ size += getDirSize(entryPath);
1729
+ }
1730
+ }
1731
+ } catch {
1732
+ }
1733
+ return size;
1734
+ };
1735
+ const BATCH_SIZE = 500;
1736
+ const existingIds = /* @__PURE__ */ new Set();
1737
+ for (let i = 0; i < folderNames.length; i += BATCH_SIZE) {
1738
+ const batch = folderNames.slice(i, i + BATCH_SIZE);
1739
+ const docs = await drive_default.find(
1740
+ { _id: { $in: batch } },
1741
+ { _id: 1 }
1742
+ ).lean();
1743
+ for (const doc of docs) {
1744
+ existingIds.add(doc._id.toString());
1745
+ }
1746
+ }
1747
+ const removed = [];
1748
+ let totalFreedInBytes = 0;
1749
+ for (const name of folderNames) {
1750
+ if (!existingIds.has(name)) {
1751
+ const dirPath = path.join(fileDir, name);
1752
+ try {
1753
+ totalFreedInBytes += getDirSize(dirPath);
1754
+ fs.rmSync(dirPath, { recursive: true, force: true });
1755
+ removed.push(name);
1756
+ } catch (e) {
1757
+ console.error(`[next-drive] Failed to remove orphaned folder ${name}:`, e);
1758
+ }
1759
+ }
1760
+ }
1761
+ const tempDir = path.join(config.storage.path, "temp");
1762
+ if (fs.existsSync(tempDir)) {
1763
+ try {
1764
+ totalFreedInBytes += getDirSize(tempDir);
1765
+ fs.rmSync(tempDir, { recursive: true, force: true });
1766
+ } catch (e) {
1767
+ console.error("[next-drive] Failed to remove temp directory:", e);
1768
+ }
1769
+ }
1770
+ const systemTmpDir = path.join(os2.tmpdir(), "next-drive-uploads");
1771
+ if (fs.existsSync(systemTmpDir)) {
1772
+ try {
1773
+ totalFreedInBytes += getDirSize(systemTmpDir);
1774
+ fs.rmSync(systemTmpDir, { recursive: true, force: true });
1775
+ } catch (e) {
1776
+ console.error("[next-drive] Failed to remove system temp directory:", e);
1777
+ }
1778
+ }
1779
+ return { removed, totalFreedInBytes };
1780
+ };
1706
1781
 
1707
1782
  // src/server/index.ts
1708
1783
  var getProvider = async (req, owner) => {
@@ -2120,7 +2195,7 @@ var driveAPIHandler = async (req, res) => {
2120
2195
  // ** 3. UPLOAD **
2121
2196
  case "upload": {
2122
2197
  if (req.method !== "POST") return res.status(405).json({ status: 405, message: "Only POST allowed" });
2123
- const systemTmpDir = path.join(os3.tmpdir(), "next-drive-uploads");
2198
+ const systemTmpDir = path.join(os2.tmpdir(), "next-drive-uploads");
2124
2199
  if (!fs.existsSync(systemTmpDir)) fs.mkdirSync(systemTmpDir, { recursive: true });
2125
2200
  const form = formidable({
2126
2201
  multiples: false,
@@ -2156,7 +2231,7 @@ var driveAPIHandler = async (req, res) => {
2156
2231
  }
2157
2232
  const { chunkIndex, totalChunks, driveId, fileName, fileSize: fileSizeInBytes, fileType, folderId } = uploadData.data;
2158
2233
  let currentUploadId = driveId;
2159
- const tempBaseDir = path.join(os3.tmpdir(), "next-drive-uploads");
2234
+ const tempBaseDir = path.join(os2.tmpdir(), "next-drive-uploads");
2160
2235
  if (!currentUploadId) {
2161
2236
  if (chunkIndex !== 0) return res.status(400).json({ message: "Missing upload ID for non-zero chunk" });
2162
2237
  if (fileType && config.security) {
@@ -2304,7 +2379,7 @@ var driveAPIHandler = async (req, res) => {
2304
2379
  const cancelData = cancelQuerySchema.safeParse(req.query);
2305
2380
  if (!cancelData.success) return res.status(400).json({ status: 400, message: "Invalid ID" });
2306
2381
  const { id } = cancelData.data;
2307
- const tempUploadDir = path.join(os3.tmpdir(), "next-drive-uploads", id);
2382
+ const tempUploadDir = path.join(os2.tmpdir(), "next-drive-uploads", id);
2308
2383
  if (fs.existsSync(tempUploadDir)) {
2309
2384
  try {
2310
2385
  fs.rmSync(tempUploadDir, { recursive: true, force: true });
@@ -2449,6 +2524,6 @@ var driveAPIHandler = async (req, res) => {
2449
2524
  }
2450
2525
  };
2451
2526
 
2452
- export { driveAPIHandler, driveConfiguration, driveDelete, driveFilePath, driveFileSchemaZod, driveGetUrl, driveInfo, driveList, driveListFiles, driveReadFile, driveUpload, drive_default, getDriveConfig, getDriveInformation };
2453
- //# sourceMappingURL=chunk-MZFX6M44.js.map
2454
- //# sourceMappingURL=chunk-MZFX6M44.js.map
2527
+ export { driveAPIHandler, driveCleanup, driveConfiguration, driveDelete, driveFilePath, driveFileSchemaZod, driveGetUrl, driveInfo, driveList, driveListFiles, driveReadFile, driveUpload, drive_default, getDriveConfig, getDriveInformation };
2528
+ //# sourceMappingURL=chunk-R43JCXQB.js.map
2529
+ //# sourceMappingURL=chunk-R43JCXQB.js.map