@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.
@@ -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 mergedConfig = {
32
- ...config,
33
- security: {
34
- maxUploadSizeInBytes: config.security?.maxUploadSizeInBytes ?? 10 * 1024 * 1024,
35
- // Default to 10MB
36
- allowedMimeTypes: config.security?.allowedMimeTypes ?? ["*/*"],
37
- signedUrls: config.security?.signedUrls,
38
- trash: config.security?.trash
39
- },
40
- information: config.information ?? (async (req) => {
41
- return {
42
- key: { id: "default-user" },
43
- storage: { quotaInBytes: 10 * 1024 * 1024 * 1024 }
44
- // Default to 10GB
45
- };
46
- })
47
- };
48
- globalConfig = mergedConfig;
49
- return mergedConfig;
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.signedUrls?.enabled) {
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
- if (!options.enforce) {
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.signedUrls?.enabled) {
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.maxUploadSizeInBytes * 2,
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 && !validateMimeType(fileType, config.security.allowedMimeTypes)) {
1543
- cleanupTempFiles(files);
1544
- return res.status(400).json({ status: 400, message: `File type ${fileType} not allowed` });
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
- const quota = await provider.getQuota(owner, accountId, information.storage.quotaInBytes);
1547
- if (quota.usedInBytes + fileSizeInBytes > quota.quotaInBytes) {
1548
- cleanupTempFiles(files);
1549
- return res.status(413).json({ status: 413, message: "Storage quota exceeded" });
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.on("error", reject);
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.on("error", reject);
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-RL3P26H3.cjs.map
1826
- //# sourceMappingURL=chunk-RL3P26H3.cjs.map
1898
+ //# sourceMappingURL=chunk-OGVU5UBC.cjs.map
1899
+ //# sourceMappingURL=chunk-OGVU5UBC.cjs.map