@muhgholy/next-drive 4.23.0 → 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 +74 -0
- package/dist/{chunk-CR3QW3QN.js → chunk-R43JCXQB.js} +130 -10
- package/dist/chunk-R43JCXQB.js.map +1 -0
- package/dist/{chunk-EUAHSB2J.cjs → chunk-RBLDH7CP.cjs} +133 -10
- package/dist/chunk-RBLDH7CP.cjs.map +1 -0
- package/dist/server/controllers/drive.d.ts +57 -0
- package/dist/server/controllers/drive.d.ts.map +1 -1
- package/dist/server/express.cjs +11 -11
- package/dist/server/express.js +2 -2
- package/dist/server/hono.cjs +11 -11
- package/dist/server/hono.js +2 -2
- package/dist/server/index.cjs +25 -13
- package/dist/server/index.d.ts +3 -1
- package/dist/server/index.d.ts.map +1 -1
- package/dist/server/index.js +1 -1
- package/package.json +2 -2
- package/dist/chunk-CR3QW3QN.js.map +0 -1
- package/dist/chunk-EUAHSB2J.cjs.map +0 -1
package/README.md
CHANGED
|
@@ -461,6 +461,80 @@ const items = await driveList({
|
|
|
461
461
|
| `limit` | `number` | No | Maximum items to return (default: 100) |
|
|
462
462
|
| `afterId` | `string` | No | Last item ID for pagination |
|
|
463
463
|
|
|
464
|
+
### List Files (Paginated)
|
|
465
|
+
|
|
466
|
+
List **files only** (not folders) with offset-based pagination:
|
|
467
|
+
|
|
468
|
+
```typescript
|
|
469
|
+
import { driveListFiles } from "@muhgholy/next-drive/server";
|
|
470
|
+
|
|
471
|
+
// List all files (page 1, default limit 50)
|
|
472
|
+
const result = await driveListFiles({});
|
|
473
|
+
|
|
474
|
+
// List files for a specific owner
|
|
475
|
+
const result = await driveListFiles({ key: { userId: "123" } });
|
|
476
|
+
|
|
477
|
+
// List files in a folder with pagination
|
|
478
|
+
const result = await driveListFiles({
|
|
479
|
+
key: { userId: "123" },
|
|
480
|
+
folderId: "folderIdHere",
|
|
481
|
+
page: 2,
|
|
482
|
+
limit: 20,
|
|
483
|
+
});
|
|
484
|
+
|
|
485
|
+
// Access pagination info
|
|
486
|
+
console.log(result.pagination);
|
|
487
|
+
// { page: 2, limit: 20, totalCount: 87, totalPages: 5, hasMore: true }
|
|
488
|
+
```
|
|
489
|
+
|
|
490
|
+
**Options:**
|
|
491
|
+
|
|
492
|
+
| Option | Type | Required | Description |
|
|
493
|
+
| ----------- | -------------------------- | -------- | ---------------------------------------------- |
|
|
494
|
+
| `key` | `Record<string, unknown>` | No | Owner key (omit to list all files) |
|
|
495
|
+
| `folderId` | `string \| null` | No | Folder ID (null/'root' for root, omit for all) |
|
|
496
|
+
| `accountId` | `string` | No | Storage account ID |
|
|
497
|
+
| `page` | `number` | No | Page number (default: 1) |
|
|
498
|
+
| `limit` | `number` | No | Items per page (default: 50, max: 100) |
|
|
499
|
+
|
|
500
|
+
**Returns:** `{ items: TDatabaseDrive[], pagination: { page, limit, totalCount, totalPages, hasMore } }`
|
|
501
|
+
|
|
502
|
+
### Direct Database Access
|
|
503
|
+
|
|
504
|
+
For custom queries, use the exposed Mongoose model:
|
|
505
|
+
|
|
506
|
+
```typescript
|
|
507
|
+
import { DatabaseMongoDrive } from "@muhgholy/next-drive/server";
|
|
508
|
+
import type { IDatabaseDriveDocument } from "@muhgholy/next-drive/server";
|
|
509
|
+
|
|
510
|
+
// Custom query
|
|
511
|
+
const files = await DatabaseMongoDrive.find({
|
|
512
|
+
owner: { userId: "123" },
|
|
513
|
+
"information.type": "FILE",
|
|
514
|
+
"information.mime": { $regex: "^image/" },
|
|
515
|
+
});
|
|
516
|
+
|
|
517
|
+
// Aggregation
|
|
518
|
+
const stats = await DatabaseMongoDrive.aggregate([
|
|
519
|
+
{ $match: { owner: { userId: "123" }, trashedAt: null } },
|
|
520
|
+
{ $group: { _id: "$information.type", count: { $sum: 1 } } },
|
|
521
|
+
]);
|
|
522
|
+
```
|
|
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
|
+
|
|
464
538
|
### Delete File or Folder
|
|
465
539
|
|
|
466
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
|
|
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(
|
|
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 = {
|
|
@@ -1451,6 +1451,51 @@ var driveList = async (options) => {
|
|
|
1451
1451
|
const items = await drive_default.find(query, {}, { sort: { order: 1, _id: -1 }, limit });
|
|
1452
1452
|
return await Promise.all(items.map((item) => item.toClient()));
|
|
1453
1453
|
};
|
|
1454
|
+
var driveListFiles = async (options) => {
|
|
1455
|
+
const { key, folderId, accountId } = options;
|
|
1456
|
+
const page = Math.max(1, options.page ?? 1);
|
|
1457
|
+
const limit = Math.min(Math.max(1, options.limit ?? 50), 100);
|
|
1458
|
+
let providerName = "LOCAL";
|
|
1459
|
+
if (accountId && accountId !== "LOCAL") {
|
|
1460
|
+
const account = await drive_default.db.model("StorageAccount").findOne({ _id: accountId, owner: key });
|
|
1461
|
+
if (!account) {
|
|
1462
|
+
throw new Error("Invalid Storage Account");
|
|
1463
|
+
}
|
|
1464
|
+
if (account.metadata.provider === "GOOGLE") {
|
|
1465
|
+
providerName = "GOOGLE";
|
|
1466
|
+
}
|
|
1467
|
+
}
|
|
1468
|
+
const query = {
|
|
1469
|
+
"provider.type": providerName,
|
|
1470
|
+
"information.type": "FILE",
|
|
1471
|
+
storageAccountId: accountId || null,
|
|
1472
|
+
trashedAt: null
|
|
1473
|
+
};
|
|
1474
|
+
if (key !== void 0) {
|
|
1475
|
+
query.owner = key;
|
|
1476
|
+
}
|
|
1477
|
+
if (folderId && folderId !== "root") {
|
|
1478
|
+
query.parentId = folderId;
|
|
1479
|
+
} else if (folderId === "root" || folderId === null) {
|
|
1480
|
+
query.parentId = null;
|
|
1481
|
+
}
|
|
1482
|
+
const skip = (page - 1) * limit;
|
|
1483
|
+
const [totalCount, items] = await Promise.all([
|
|
1484
|
+
drive_default.countDocuments(query),
|
|
1485
|
+
drive_default.find(query, {}, { sort: { createdAt: -1 }, skip, limit })
|
|
1486
|
+
]);
|
|
1487
|
+
const totalPages = Math.ceil(totalCount / limit);
|
|
1488
|
+
return {
|
|
1489
|
+
items: await Promise.all(items.map((item) => item.toClient())),
|
|
1490
|
+
pagination: {
|
|
1491
|
+
page,
|
|
1492
|
+
limit,
|
|
1493
|
+
totalCount,
|
|
1494
|
+
totalPages,
|
|
1495
|
+
hasMore: page < totalPages
|
|
1496
|
+
}
|
|
1497
|
+
};
|
|
1498
|
+
};
|
|
1454
1499
|
var driveDelete = async (source, options) => {
|
|
1455
1500
|
const { recurse = true } = options || {};
|
|
1456
1501
|
let drive;
|
|
@@ -1549,7 +1594,7 @@ var driveUpload = async (source, key, options) => {
|
|
|
1549
1594
|
const stats = fs.statSync(source);
|
|
1550
1595
|
fileSize = stats.size;
|
|
1551
1596
|
} else if (Buffer.isBuffer(source)) {
|
|
1552
|
-
const tempDir = path.join(
|
|
1597
|
+
const tempDir = path.join(os2.tmpdir(), "next-drive-uploads");
|
|
1553
1598
|
if (!fs.existsSync(tempDir)) {
|
|
1554
1599
|
fs.mkdirSync(tempDir, { recursive: true });
|
|
1555
1600
|
}
|
|
@@ -1558,7 +1603,7 @@ var driveUpload = async (source, key, options) => {
|
|
|
1558
1603
|
sourceFilePath = tempFilePath;
|
|
1559
1604
|
fileSize = source.length;
|
|
1560
1605
|
} else {
|
|
1561
|
-
const tempDir = path.join(
|
|
1606
|
+
const tempDir = path.join(os2.tmpdir(), "next-drive-uploads");
|
|
1562
1607
|
if (!fs.existsSync(tempDir)) {
|
|
1563
1608
|
fs.mkdirSync(tempDir, { recursive: true });
|
|
1564
1609
|
}
|
|
@@ -1658,6 +1703,81 @@ var driveUpload = async (source, key, options) => {
|
|
|
1658
1703
|
}
|
|
1659
1704
|
}
|
|
1660
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
|
+
};
|
|
1661
1781
|
|
|
1662
1782
|
// src/server/index.ts
|
|
1663
1783
|
var getProvider = async (req, owner) => {
|
|
@@ -2075,7 +2195,7 @@ var driveAPIHandler = async (req, res) => {
|
|
|
2075
2195
|
// ** 3. UPLOAD **
|
|
2076
2196
|
case "upload": {
|
|
2077
2197
|
if (req.method !== "POST") return res.status(405).json({ status: 405, message: "Only POST allowed" });
|
|
2078
|
-
const systemTmpDir = path.join(
|
|
2198
|
+
const systemTmpDir = path.join(os2.tmpdir(), "next-drive-uploads");
|
|
2079
2199
|
if (!fs.existsSync(systemTmpDir)) fs.mkdirSync(systemTmpDir, { recursive: true });
|
|
2080
2200
|
const form = formidable({
|
|
2081
2201
|
multiples: false,
|
|
@@ -2111,7 +2231,7 @@ var driveAPIHandler = async (req, res) => {
|
|
|
2111
2231
|
}
|
|
2112
2232
|
const { chunkIndex, totalChunks, driveId, fileName, fileSize: fileSizeInBytes, fileType, folderId } = uploadData.data;
|
|
2113
2233
|
let currentUploadId = driveId;
|
|
2114
|
-
const tempBaseDir = path.join(
|
|
2234
|
+
const tempBaseDir = path.join(os2.tmpdir(), "next-drive-uploads");
|
|
2115
2235
|
if (!currentUploadId) {
|
|
2116
2236
|
if (chunkIndex !== 0) return res.status(400).json({ message: "Missing upload ID for non-zero chunk" });
|
|
2117
2237
|
if (fileType && config.security) {
|
|
@@ -2259,7 +2379,7 @@ var driveAPIHandler = async (req, res) => {
|
|
|
2259
2379
|
const cancelData = cancelQuerySchema.safeParse(req.query);
|
|
2260
2380
|
if (!cancelData.success) return res.status(400).json({ status: 400, message: "Invalid ID" });
|
|
2261
2381
|
const { id } = cancelData.data;
|
|
2262
|
-
const tempUploadDir = path.join(
|
|
2382
|
+
const tempUploadDir = path.join(os2.tmpdir(), "next-drive-uploads", id);
|
|
2263
2383
|
if (fs.existsSync(tempUploadDir)) {
|
|
2264
2384
|
try {
|
|
2265
2385
|
fs.rmSync(tempUploadDir, { recursive: true, force: true });
|
|
@@ -2404,6 +2524,6 @@ var driveAPIHandler = async (req, res) => {
|
|
|
2404
2524
|
}
|
|
2405
2525
|
};
|
|
2406
2526
|
|
|
2407
|
-
export { driveAPIHandler, driveConfiguration, driveDelete, driveFilePath, driveFileSchemaZod, driveGetUrl, driveInfo, driveList, driveReadFile, driveUpload, getDriveConfig, getDriveInformation };
|
|
2408
|
-
//# sourceMappingURL=chunk-
|
|
2409
|
-
//# sourceMappingURL=chunk-
|
|
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
|