@muhgholy/next-drive 3.9.0 → 3.11.0
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/dist/{chunk-RL3P26H3.cjs → chunk-OGVU5UBC.cjs} +122 -49
- package/dist/chunk-OGVU5UBC.cjs.map +1 -0
- package/dist/{chunk-WPX4ZQ4V.js → chunk-RGE7EYMF.js} +122 -49
- package/dist/chunk-RGE7EYMF.js.map +1 -0
- package/dist/server/config.d.ts.map +1 -1
- 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/index.cjs +13 -13
- package/dist/server/index.d.ts.map +1 -1
- package/dist/server/index.js +1 -1
- package/dist/server/providers/google.d.ts.map +1 -1
- package/dist/server/providers/local.d.ts.map +1 -1
- package/dist/types/server/config.d.ts +14 -3
- package/dist/types/server/config.d.ts.map +1 -1
- package/dist/types/server/express.d.ts +40 -15
- package/dist/types/server/express.d.ts.map +1 -1
- package/package.json +1 -1
- package/dist/chunk-RL3P26H3.cjs.map +0 -1
- package/dist/chunk-WPX4ZQ4V.js.map +0 -1
|
@@ -28,25 +28,35 @@ var driveConfiguration = (config) => {
|
|
|
28
28
|
if (mongoose2__default.default.connection.readyState !== 1) {
|
|
29
29
|
throw new Error("Database not connected. Please connect to Mongoose before initializing next-drive.");
|
|
30
30
|
}
|
|
31
|
-
const
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
31
|
+
const mode = config.mode || "NORMAL";
|
|
32
|
+
if (mode === "ROOT") {
|
|
33
|
+
globalConfig = {
|
|
34
|
+
...config,
|
|
35
|
+
mode: "ROOT",
|
|
36
|
+
security: config.security || {
|
|
37
|
+
maxUploadSizeInBytes: 1024 * 1024 * 1024 * 10,
|
|
38
|
+
// 10GB default for ROOT
|
|
39
|
+
allowedMimeTypes: ["*/*"]
|
|
40
|
+
}
|
|
41
|
+
};
|
|
42
|
+
return globalConfig;
|
|
43
|
+
} else {
|
|
44
|
+
if (!config.information) {
|
|
45
|
+
throw new Error("information callback is required in NORMAL mode");
|
|
46
|
+
}
|
|
47
|
+
globalConfig = {
|
|
48
|
+
...config,
|
|
49
|
+
mode: "NORMAL",
|
|
50
|
+
security: {
|
|
51
|
+
maxUploadSizeInBytes: config.security?.maxUploadSizeInBytes ?? 10 * 1024 * 1024,
|
|
52
|
+
allowedMimeTypes: config.security?.allowedMimeTypes ?? ["*/*"],
|
|
53
|
+
signedUrls: config.security?.signedUrls,
|
|
54
|
+
trash: config.security?.trash
|
|
55
|
+
},
|
|
56
|
+
information: config.information
|
|
57
|
+
};
|
|
58
|
+
return globalConfig;
|
|
59
|
+
}
|
|
50
60
|
};
|
|
51
61
|
var getDriveConfig = () => {
|
|
52
62
|
if (!globalConfig) throw new Error("Drive configuration not initialized");
|
|
@@ -54,6 +64,16 @@ var getDriveConfig = () => {
|
|
|
54
64
|
};
|
|
55
65
|
var getDriveInformation = async (req) => {
|
|
56
66
|
const config = getDriveConfig();
|
|
67
|
+
if (config.mode === "ROOT") {
|
|
68
|
+
if (!config.information) {
|
|
69
|
+
return {
|
|
70
|
+
key: null,
|
|
71
|
+
storage: { quotaInBytes: Number.MAX_SAFE_INTEGER }
|
|
72
|
+
// Unlimited quota in ROOT mode
|
|
73
|
+
};
|
|
74
|
+
}
|
|
75
|
+
return config.information(req);
|
|
76
|
+
}
|
|
57
77
|
return config.information(req);
|
|
58
78
|
};
|
|
59
79
|
var informationSchema = new mongoose2.Schema({
|
|
@@ -265,19 +285,25 @@ var LocalStorageProvider = {
|
|
|
265
285
|
search: async (query, owner, accountId) => {
|
|
266
286
|
},
|
|
267
287
|
getQuota: async (owner, accountId, configuredQuotaInBytes) => {
|
|
288
|
+
const config = getDriveConfig();
|
|
289
|
+
const isRootMode = config.mode === "ROOT";
|
|
290
|
+
const match = {
|
|
291
|
+
"information.type": "FILE",
|
|
292
|
+
trashedAt: null,
|
|
293
|
+
"provider.type": "LOCAL",
|
|
294
|
+
storageAccountId: accountId || null
|
|
295
|
+
};
|
|
296
|
+
if (!isRootMode) {
|
|
297
|
+
match.owner = owner;
|
|
298
|
+
}
|
|
268
299
|
const result = await drive_default.aggregate([
|
|
269
|
-
{
|
|
270
|
-
$match: {
|
|
271
|
-
owner,
|
|
272
|
-
"information.type": "FILE",
|
|
273
|
-
trashedAt: null,
|
|
274
|
-
"provider.type": "LOCAL",
|
|
275
|
-
storageAccountId: accountId || null
|
|
276
|
-
}
|
|
277
|
-
},
|
|
300
|
+
{ $match: match },
|
|
278
301
|
{ $group: { _id: null, total: { $sum: "$information.sizeInBytes" } } }
|
|
279
302
|
]);
|
|
280
303
|
const usedInBytes = result[0]?.total || 0;
|
|
304
|
+
if (isRootMode) {
|
|
305
|
+
return { usedInBytes, quotaInBytes: Number.MAX_SAFE_INTEGER };
|
|
306
|
+
}
|
|
281
307
|
return { usedInBytes, quotaInBytes: configuredQuotaInBytes ?? 0 };
|
|
282
308
|
},
|
|
283
309
|
openStream: async (item, accountId) => {
|
|
@@ -619,6 +645,23 @@ var GoogleDriveProvider = {
|
|
|
619
645
|
}
|
|
620
646
|
},
|
|
621
647
|
getQuota: async (owner, accountId, _configuredQuotaInBytes) => {
|
|
648
|
+
const config = getDriveConfig();
|
|
649
|
+
const isRootMode = config.mode === "ROOT";
|
|
650
|
+
if (isRootMode) {
|
|
651
|
+
const result = await drive_default.aggregate([
|
|
652
|
+
{
|
|
653
|
+
$match: {
|
|
654
|
+
"information.type": "FILE",
|
|
655
|
+
trashedAt: null,
|
|
656
|
+
"provider.type": "GOOGLE",
|
|
657
|
+
storageAccountId: accountId || null
|
|
658
|
+
}
|
|
659
|
+
},
|
|
660
|
+
{ $group: { _id: null, total: { $sum: "$information.sizeInBytes" } } }
|
|
661
|
+
]);
|
|
662
|
+
const usedInBytes = result[0]?.total || 0;
|
|
663
|
+
return { usedInBytes, quotaInBytes: Number.MAX_SAFE_INTEGER };
|
|
664
|
+
}
|
|
622
665
|
try {
|
|
623
666
|
const { client } = await createAuthClient(owner, accountId);
|
|
624
667
|
const drive = googleapis.google.drive({ version: "v3", auth: client });
|
|
@@ -845,7 +888,7 @@ var getNextOrderValue = async (owner) => {
|
|
|
845
888
|
};
|
|
846
889
|
var driveGetUrl = (fileId, options) => {
|
|
847
890
|
const config = getDriveConfig();
|
|
848
|
-
if (!config.security
|
|
891
|
+
if (!config.security?.signedUrls?.enabled) {
|
|
849
892
|
return `/api/drive?action=serve&id=${fileId}`;
|
|
850
893
|
}
|
|
851
894
|
const { secret, expiresIn } = config.security.signedUrls;
|
|
@@ -1132,13 +1175,14 @@ var driveUpload = async (source, key, options) => {
|
|
|
1132
1175
|
};
|
|
1133
1176
|
mimeType = mimeTypes[ext] || "application/octet-stream";
|
|
1134
1177
|
}
|
|
1135
|
-
if (!validateMimeType(mimeType, config.security.allowedMimeTypes)) {
|
|
1178
|
+
if (config.security && !validateMimeType(mimeType, config.security.allowedMimeTypes)) {
|
|
1136
1179
|
throw new Error(`File type ${mimeType} not allowed`);
|
|
1137
1180
|
}
|
|
1138
|
-
if (fileSize > config.security.maxUploadSizeInBytes) {
|
|
1181
|
+
if (config.security && fileSize > config.security.maxUploadSizeInBytes) {
|
|
1139
1182
|
throw new Error(`File size ${fileSize} exceeds maximum allowed size ${config.security.maxUploadSizeInBytes}`);
|
|
1140
1183
|
}
|
|
1141
|
-
|
|
1184
|
+
const isRootMode = config.mode === "ROOT";
|
|
1185
|
+
if (!options.enforce && !isRootMode) {
|
|
1142
1186
|
const quota = await provider.getQuota(key, accountId, void 0);
|
|
1143
1187
|
if (quota.usedInBytes + fileSize > quota.quotaInBytes) {
|
|
1144
1188
|
throw new Error("Storage quota exceeded");
|
|
@@ -1264,7 +1308,7 @@ var driveAPIHandler = async (req, res) => {
|
|
|
1264
1308
|
}
|
|
1265
1309
|
const drive = await drive_default.findById(id);
|
|
1266
1310
|
if (!drive) return res.status(404).json({ status: 404, message: "File not found" });
|
|
1267
|
-
if (config.security
|
|
1311
|
+
if (config.security?.signedUrls?.enabled) {
|
|
1268
1312
|
if (!token || typeof token !== "string") {
|
|
1269
1313
|
return res.status(401).json({ status: 401, message: "Missing or invalid token" });
|
|
1270
1314
|
}
|
|
@@ -1316,9 +1360,11 @@ var driveAPIHandler = async (req, res) => {
|
|
|
1316
1360
|
}
|
|
1317
1361
|
}
|
|
1318
1362
|
try {
|
|
1363
|
+
const mode = config.mode || "NORMAL";
|
|
1319
1364
|
const information = await getDriveInformation(req);
|
|
1320
1365
|
const { key: owner } = information;
|
|
1321
1366
|
const STORAGE_PATH = config.storage.path;
|
|
1367
|
+
const isRootMode = mode === "ROOT";
|
|
1322
1368
|
if (action === "information") {
|
|
1323
1369
|
const { clientId, clientSecret, redirectUri } = config.storage?.google || {};
|
|
1324
1370
|
const googleConfigured = !!(clientId && clientSecret && redirectUri);
|
|
@@ -1328,7 +1374,8 @@ var driveAPIHandler = async (req, res) => {
|
|
|
1328
1374
|
data: {
|
|
1329
1375
|
providers: {
|
|
1330
1376
|
google: googleConfigured
|
|
1331
|
-
}
|
|
1377
|
+
},
|
|
1378
|
+
mode
|
|
1332
1379
|
}
|
|
1333
1380
|
});
|
|
1334
1381
|
}
|
|
@@ -1461,12 +1508,14 @@ var driveAPIHandler = async (req, res) => {
|
|
|
1461
1508
|
console.error("Sync failed", e);
|
|
1462
1509
|
}
|
|
1463
1510
|
const query = {
|
|
1464
|
-
owner,
|
|
1465
1511
|
"provider.type": provider.name,
|
|
1466
1512
|
storageAccountId: accountId || null,
|
|
1467
1513
|
parentId: folderId === "root" || !folderId ? null : folderId,
|
|
1468
1514
|
trashedAt: null
|
|
1469
1515
|
};
|
|
1516
|
+
if (!isRootMode) {
|
|
1517
|
+
query.owner = owner;
|
|
1518
|
+
}
|
|
1470
1519
|
if (afterId) query._id = { $lt: afterId };
|
|
1471
1520
|
const items = await drive_default.find(query, {}, { sort: { order: 1, _id: -1 }, limit });
|
|
1472
1521
|
const plainItems = await Promise.all(items.map((item) => item.toClient()));
|
|
@@ -1486,12 +1535,14 @@ var driveAPIHandler = async (req, res) => {
|
|
|
1486
1535
|
}
|
|
1487
1536
|
}
|
|
1488
1537
|
const query = {
|
|
1489
|
-
owner,
|
|
1490
1538
|
"provider.type": provider.name,
|
|
1491
1539
|
storageAccountId: accountId || null,
|
|
1492
1540
|
trashedAt: trashed ? { $ne: null } : null,
|
|
1493
1541
|
name: { $regex: q, $options: "i" }
|
|
1494
1542
|
};
|
|
1543
|
+
if (!isRootMode) {
|
|
1544
|
+
query.owner = owner;
|
|
1545
|
+
}
|
|
1495
1546
|
if (folderId && folderId !== "root") query.parentId = folderId;
|
|
1496
1547
|
const items = await drive_default.find(query, {}, { limit, sort: { createdAt: -1 } });
|
|
1497
1548
|
const plainItems = await Promise.all(items.map((i) => i.toClient()));
|
|
@@ -1504,7 +1555,7 @@ var driveAPIHandler = async (req, res) => {
|
|
|
1504
1555
|
if (!fs4__default.default.existsSync(systemTmpDir)) fs4__default.default.mkdirSync(systemTmpDir, { recursive: true });
|
|
1505
1556
|
const form = formidable__default.default({
|
|
1506
1557
|
multiples: false,
|
|
1507
|
-
maxFileSize: config.security
|
|
1558
|
+
maxFileSize: (config.security?.maxUploadSizeInBytes ?? 1024 * 1024 * 1024) * 2,
|
|
1508
1559
|
uploadDir: systemTmpDir,
|
|
1509
1560
|
keepExtensions: true
|
|
1510
1561
|
});
|
|
@@ -1539,14 +1590,18 @@ var driveAPIHandler = async (req, res) => {
|
|
|
1539
1590
|
const tempBaseDir = path3__default.default.join(os2__default.default.tmpdir(), "next-drive-uploads");
|
|
1540
1591
|
if (!currentUploadId) {
|
|
1541
1592
|
if (chunkIndex !== 0) return res.status(400).json({ message: "Missing upload ID for non-zero chunk" });
|
|
1542
|
-
if (fileType &&
|
|
1543
|
-
|
|
1544
|
-
|
|
1593
|
+
if (fileType && config.security) {
|
|
1594
|
+
if (!validateMimeType(fileType, config.security.allowedMimeTypes)) {
|
|
1595
|
+
cleanupTempFiles(files);
|
|
1596
|
+
return res.status(400).json({ status: 400, message: `File type ${fileType} not allowed` });
|
|
1597
|
+
}
|
|
1545
1598
|
}
|
|
1546
|
-
|
|
1547
|
-
|
|
1548
|
-
|
|
1549
|
-
|
|
1599
|
+
if (!isRootMode) {
|
|
1600
|
+
const quota = await provider.getQuota(owner, accountId, information.storage.quotaInBytes);
|
|
1601
|
+
if (quota.usedInBytes + fileSizeInBytes > quota.quotaInBytes) {
|
|
1602
|
+
cleanupTempFiles(files);
|
|
1603
|
+
return res.status(413).json({ status: 413, message: "Storage quota exceeded" });
|
|
1604
|
+
}
|
|
1550
1605
|
}
|
|
1551
1606
|
currentUploadId = crypto2__default.default.randomUUID();
|
|
1552
1607
|
const uploadDir = path3__default.default.join(tempBaseDir, currentUploadId);
|
|
@@ -1589,23 +1644,41 @@ var driveAPIHandler = async (req, res) => {
|
|
|
1589
1644
|
const meta = JSON.parse(fs4__default.default.readFileSync(metaPath, "utf-8"));
|
|
1590
1645
|
const finalTempPath = path3__default.default.join(uploadDir, "final.bin");
|
|
1591
1646
|
const writeStream = fs4__default.default.createWriteStream(finalTempPath);
|
|
1647
|
+
let streamError = null;
|
|
1648
|
+
writeStream.on("error", (err) => {
|
|
1649
|
+
streamError = err;
|
|
1650
|
+
});
|
|
1592
1651
|
await new Promise((resolve, reject) => {
|
|
1593
1652
|
writeStream.on("open", () => resolve());
|
|
1594
|
-
writeStream.
|
|
1653
|
+
writeStream.once("error", reject);
|
|
1595
1654
|
});
|
|
1596
1655
|
for (let i = 0; i < totalChunks; i++) {
|
|
1656
|
+
if (streamError) {
|
|
1657
|
+
writeStream.destroy();
|
|
1658
|
+
throw streamError;
|
|
1659
|
+
}
|
|
1597
1660
|
const pPath = path3__default.default.join(uploadDir, `part_${i}`);
|
|
1598
1661
|
if (!fs4__default.default.existsSync(pPath)) {
|
|
1599
1662
|
writeStream.destroy();
|
|
1600
1663
|
throw new Error(`Missing chunk part: ${i}`);
|
|
1601
1664
|
}
|
|
1602
1665
|
const data = fs4__default.default.readFileSync(pPath);
|
|
1603
|
-
writeStream.write(data);
|
|
1666
|
+
const canContinue = writeStream.write(data);
|
|
1667
|
+
if (!canContinue) {
|
|
1668
|
+
await new Promise((resolve, reject) => {
|
|
1669
|
+
writeStream.once("drain", resolve);
|
|
1670
|
+
writeStream.once("error", reject);
|
|
1671
|
+
});
|
|
1672
|
+
}
|
|
1604
1673
|
}
|
|
1605
1674
|
await new Promise((resolve, reject) => {
|
|
1675
|
+
if (streamError) {
|
|
1676
|
+
reject(streamError);
|
|
1677
|
+
return;
|
|
1678
|
+
}
|
|
1606
1679
|
writeStream.end();
|
|
1607
1680
|
writeStream.on("finish", resolve);
|
|
1608
|
-
writeStream.
|
|
1681
|
+
writeStream.once("error", reject);
|
|
1609
1682
|
});
|
|
1610
1683
|
if (!fs4__default.default.existsSync(finalTempPath)) {
|
|
1611
1684
|
throw new Error("Failed to create merged file");
|
|
@@ -1822,5 +1895,5 @@ exports.driveReadFile = driveReadFile;
|
|
|
1822
1895
|
exports.driveUpload = driveUpload;
|
|
1823
1896
|
exports.getDriveConfig = getDriveConfig;
|
|
1824
1897
|
exports.getDriveInformation = getDriveInformation;
|
|
1825
|
-
//# sourceMappingURL=chunk-
|
|
1826
|
-
//# sourceMappingURL=chunk-
|
|
1898
|
+
//# sourceMappingURL=chunk-OGVU5UBC.cjs.map
|
|
1899
|
+
//# sourceMappingURL=chunk-OGVU5UBC.cjs.map
|