@muhgholy/next-drive 4.15.1 → 4.17.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.
@@ -2,7 +2,7 @@ import formidable from 'formidable';
2
2
  import path from 'path';
3
3
  import fs from 'fs';
4
4
  import os3 from 'os';
5
- import crypto3 from 'crypto';
5
+ import crypto2 from 'crypto';
6
6
  import mongoose, { Schema, isValidObjectId } from 'mongoose';
7
7
  import sharp2 from 'sharp';
8
8
  import { z } from 'zod';
@@ -238,38 +238,49 @@ var runMigrations = async (storagePath) => {
238
238
  };
239
239
 
240
240
  // src/server/config.ts
241
- var globalConfig = null;
242
- var migrationPromise = null;
243
- var configInitialized = false;
241
+ var GLOBAL_KEY = "__nextDrive";
242
+ var getGlobal = () => {
243
+ if (!globalThis[GLOBAL_KEY]) {
244
+ globalThis[GLOBAL_KEY] = {
245
+ config: null,
246
+ migrationPromise: null,
247
+ initialized: false
248
+ };
249
+ }
250
+ return globalThis[GLOBAL_KEY];
251
+ };
244
252
  var driveConfiguration = async (config) => {
253
+ const g = getGlobal();
245
254
  if (mongoose.connection.readyState !== 1) {
246
255
  throw new Error("Database not connected. Please connect to Mongoose before initializing next-drive.");
247
256
  }
248
- if (configInitialized && globalConfig) {
249
- if (migrationPromise) await migrationPromise;
250
- return globalConfig;
257
+ if (g.initialized && g.config) {
258
+ if (g.migrationPromise) await g.migrationPromise;
259
+ return g.config;
251
260
  }
252
261
  const resolvedPath = config.storage?.path || path.join(os3.tmpdir(), "next-drive-data");
253
262
  const mode = config.mode || "NORMAL";
254
263
  if (mode === "ROOT") {
255
- globalConfig = {
264
+ g.config = {
256
265
  ...config,
257
266
  mode: "ROOT",
258
267
  storage: {
259
268
  ...config.storage,
260
269
  path: resolvedPath
261
270
  },
262
- security: config.security || {
263
- maxUploadSizeInBytes: 1024 * 1024 * 1024 * 10,
271
+ security: {
272
+ maxUploadSizeInBytes: config.security?.maxUploadSizeInBytes ?? 1024 * 1024 * 1024 * 10,
264
273
  // 10GB default for ROOT
265
- allowedMimeTypes: ["*/*"]
274
+ allowedMimeTypes: config.security?.allowedMimeTypes ?? ["*/*"],
275
+ signedUrls: config.security?.signedUrls,
276
+ trash: config.security?.trash
266
277
  }
267
278
  };
268
279
  } else {
269
280
  if (!config.information) {
270
281
  throw new Error("information callback is required in NORMAL mode");
271
282
  }
272
- globalConfig = {
283
+ g.config = {
273
284
  ...config,
274
285
  mode: "NORMAL",
275
286
  storage: {
@@ -285,16 +296,17 @@ var driveConfiguration = async (config) => {
285
296
  information: config.information
286
297
  };
287
298
  }
288
- configInitialized = true;
289
- if (!migrationPromise) {
290
- migrationPromise = runMigrations(resolvedPath);
299
+ g.initialized = true;
300
+ if (!g.migrationPromise) {
301
+ g.migrationPromise = runMigrations(resolvedPath);
291
302
  }
292
- await migrationPromise;
293
- return globalConfig;
303
+ await g.migrationPromise;
304
+ return g.config;
294
305
  };
295
306
  var getDriveConfig = () => {
296
- if (!globalConfig) throw new Error("Drive configuration not initialized");
297
- return globalConfig;
307
+ const g = getGlobal();
308
+ if (!g.config) throw new Error("Drive configuration not initialized");
309
+ return g.config;
298
310
  };
299
311
  var getDriveInformation = async (req) => {
300
312
  const config = getDriveConfig();
@@ -351,7 +363,7 @@ var validateMimeType = (mime, allowedTypes) => {
351
363
  });
352
364
  };
353
365
  var computeFileHash = (filePath) => new Promise((resolve, reject) => {
354
- const hash = crypto3.createHash("sha256");
366
+ const hash = crypto2.createHash("sha256");
355
367
  const stream = fs.createReadStream(filePath);
356
368
  stream.on("data", (data) => hash.update(data));
357
369
  stream.on("end", () => resolve(hash.digest("hex")));
@@ -1273,7 +1285,7 @@ var driveGetUrl = (fileId, options) => {
1273
1285
  } else {
1274
1286
  expiryTimestamp = Math.floor(Date.now() / 1e3) + expiresIn;
1275
1287
  }
1276
- const signature = crypto3.createHmac("sha256", secret).update(`${fileId}:${expiryTimestamp}`).digest("hex");
1288
+ const signature = crypto2.createHmac("sha256", secret).update(`${fileId}:${expiryTimestamp}`).digest("hex");
1277
1289
  const token = Buffer.from(`${expiryTimestamp}:${signature}`).toString("base64url");
1278
1290
  return `/api/drive?action=serve&id=${fileId}&token=${token}`;
1279
1291
  };
@@ -1539,7 +1551,7 @@ var driveUpload = async (source, key, options) => {
1539
1551
  if (!fs.existsSync(tempDir)) {
1540
1552
  fs.mkdirSync(tempDir, { recursive: true });
1541
1553
  }
1542
- tempFilePath = path.join(tempDir, `upload-${crypto3.randomUUID()}.tmp`);
1554
+ tempFilePath = path.join(tempDir, `upload-${crypto2.randomUUID()}.tmp`);
1543
1555
  fs.writeFileSync(tempFilePath, source);
1544
1556
  sourceFilePath = tempFilePath;
1545
1557
  fileSize = source.length;
@@ -1548,7 +1560,7 @@ var driveUpload = async (source, key, options) => {
1548
1560
  if (!fs.existsSync(tempDir)) {
1549
1561
  fs.mkdirSync(tempDir, { recursive: true });
1550
1562
  }
1551
- tempFilePath = path.join(tempDir, `upload-${crypto3.randomUUID()}.tmp`);
1563
+ tempFilePath = path.join(tempDir, `upload-${crypto2.randomUUID()}.tmp`);
1552
1564
  const writeStream = fs.createWriteStream(tempFilePath);
1553
1565
  await new Promise((resolve, reject) => {
1554
1566
  source.pipe(writeStream);
@@ -1656,6 +1668,21 @@ var getProvider = async (req, owner) => {
1656
1668
  if (account.metadata.provider === "GOOGLE") return { provider: GoogleDriveProvider, accountId: account._id.toString() };
1657
1669
  return { provider: LocalStorageProvider };
1658
1670
  };
1671
+ var addSignedUrlToken = (item, config) => {
1672
+ if (!config.security?.signedUrls?.enabled || !config.security.signedUrls.secret) {
1673
+ return item;
1674
+ }
1675
+ const { secret, expiresIn } = config.security.signedUrls;
1676
+ const expiryTimestamp = Math.floor(Date.now() / 1e3) + expiresIn;
1677
+ const signature = crypto2.createHmac("sha256", secret).update(`${item.id}:${expiryTimestamp}`).digest("hex");
1678
+ return { ...item, token: Buffer.from(`${expiryTimestamp}:${signature}`).toString("base64url") };
1679
+ };
1680
+ var addSignedUrlTokens = (items, config) => {
1681
+ if (!config.security?.signedUrls?.enabled || !config.security.signedUrls.secret) {
1682
+ return items;
1683
+ }
1684
+ return items.map((item) => addSignedUrlToken(item, config));
1685
+ };
1659
1686
  var applyCorsHeaders = (req, res, config) => {
1660
1687
  const cors = config.cors;
1661
1688
  if (!cors?.enabled) return false;
@@ -1737,7 +1764,7 @@ var driveAPIHandler = async (req, res) => {
1737
1764
  return res.status(401).json({ status: 401, message: "Token expired" });
1738
1765
  }
1739
1766
  const { secret } = config.security.signedUrls;
1740
- const expectedSignature = crypto3.createHmac("sha256", secret).update(`${id}:${expiry}`).digest("hex");
1767
+ const expectedSignature = crypto2.createHmac("sha256", secret).update(`${id}:${expiry}`).digest("hex");
1741
1768
  if (signature !== expectedSignature) {
1742
1769
  return res.status(401).json({ status: 401, message: "Invalid token" });
1743
1770
  }
@@ -2011,15 +2038,7 @@ var driveAPIHandler = async (req, res) => {
2011
2038
  }
2012
2039
  if (afterId) query._id = { $lt: afterId };
2013
2040
  const items = await drive_default.find(query, {}, { sort: { order: 1, _id: -1 }, limit });
2014
- let plainItems = await Promise.all(items.map((item) => item.toClient()));
2015
- if (config.security?.signedUrls?.enabled && config.security.signedUrls.secret) {
2016
- const { secret, expiresIn } = config.security.signedUrls;
2017
- plainItems = plainItems.map((item) => {
2018
- const expiryTimestamp = Math.floor(Date.now() / 1e3) + expiresIn;
2019
- const signature = crypto3.createHmac("sha256", secret).update(`${item.id}:${expiryTimestamp}`).digest("hex");
2020
- return { ...item, token: Buffer.from(`${expiryTimestamp}:${signature}`).toString("base64url") };
2021
- });
2022
- }
2041
+ const plainItems = addSignedUrlTokens(await Promise.all(items.map((item) => item.toClient())), config);
2023
2042
  res.status(200).json({ status: 200, message: "Items retrieved", data: { items: plainItems, hasMore: items.length === limit } });
2024
2043
  return;
2025
2044
  }
@@ -2046,15 +2065,7 @@ var driveAPIHandler = async (req, res) => {
2046
2065
  }
2047
2066
  if (folderId && folderId !== "root") query.parentId = folderId;
2048
2067
  const items = await drive_default.find(query, {}, { limit, sort: { createdAt: -1 } });
2049
- let plainItems = await Promise.all(items.map((i) => i.toClient()));
2050
- if (config.security?.signedUrls?.enabled && config.security.signedUrls.secret) {
2051
- const { secret, expiresIn } = config.security.signedUrls;
2052
- plainItems = plainItems.map((item) => {
2053
- const expiryTimestamp = Math.floor(Date.now() / 1e3) + expiresIn;
2054
- const signature = crypto3.createHmac("sha256", secret).update(`${item.id}:${expiryTimestamp}`).digest("hex");
2055
- return { ...item, token: Buffer.from(`${expiryTimestamp}:${signature}`).toString("base64url") };
2056
- });
2057
- }
2068
+ const plainItems = addSignedUrlTokens(await Promise.all(items.map((i) => i.toClient())), config);
2058
2069
  return res.status(200).json({ status: 200, message: "Results", data: { items: plainItems } });
2059
2070
  }
2060
2071
  // ** 3. UPLOAD **
@@ -2112,7 +2123,7 @@ var driveAPIHandler = async (req, res) => {
2112
2123
  return res.status(413).json({ status: 413, message: "Storage quota exceeded" });
2113
2124
  }
2114
2125
  }
2115
- currentUploadId = crypto3.randomUUID();
2126
+ currentUploadId = crypto2.randomUUID();
2116
2127
  const uploadDir = path.join(tempBaseDir, currentUploadId);
2117
2128
  fs.mkdirSync(uploadDir, { recursive: true });
2118
2129
  const metadata = {
@@ -2217,7 +2228,7 @@ var driveAPIHandler = async (req, res) => {
2217
2228
  const item = await provider.uploadFile(drive, finalTempPath, meta.accountId);
2218
2229
  fs.rmSync(uploadDir, { recursive: true, force: true });
2219
2230
  const newQuota = await provider.getQuota(meta.owner, meta.accountId, information.storage.quotaInBytes);
2220
- res.status(200).json({ status: 200, message: "Upload complete", data: { type: "UPLOAD_COMPLETE", driveId: String(drive._id), item }, statistic: { storage: newQuota } });
2231
+ res.status(200).json({ status: 200, message: "Upload complete", data: { type: "UPLOAD_COMPLETE", driveId: String(drive._id), item: addSignedUrlToken(item, config) }, statistic: { storage: newQuota } });
2221
2232
  } catch (err) {
2222
2233
  await drive_default.deleteOne({ _id: drive._id });
2223
2234
  throw err;
@@ -2259,7 +2270,7 @@ var driveAPIHandler = async (req, res) => {
2259
2270
  const folderData = createFolderBodySchema.safeParse(req.body);
2260
2271
  if (!folderData.success) return res.status(400).json({ status: 400, message: folderData.error.errors[0].message });
2261
2272
  const { name, parentId } = folderData.data;
2262
- const item = await provider.createFolder(name, parentId ?? null, owner, accountId);
2273
+ const item = addSignedUrlToken(await provider.createFolder(name, parentId ?? null, owner, accountId), config);
2263
2274
  return res.status(201).json({ status: 201, message: "Folder created", data: { item } });
2264
2275
  }
2265
2276
  // ** 5. DELETE **
@@ -2314,15 +2325,7 @@ var driveAPIHandler = async (req, res) => {
2314
2325
  trashedAt: { $ne: null }
2315
2326
  };
2316
2327
  const items = await drive_default.find(query, {}, { sort: { trashedAt: -1 } });
2317
- let plainItems = await Promise.all(items.map((item) => item.toClient()));
2318
- if (config.security?.signedUrls?.enabled && config.security.signedUrls.secret) {
2319
- const { secret, expiresIn } = config.security.signedUrls;
2320
- plainItems = plainItems.map((item) => {
2321
- const expiryTimestamp = Math.floor(Date.now() / 1e3) + expiresIn;
2322
- const signature = crypto3.createHmac("sha256", secret).update(`${item.id}:${expiryTimestamp}`).digest("hex");
2323
- return { ...item, token: Buffer.from(`${expiryTimestamp}:${signature}`).toString("base64url") };
2324
- });
2325
- }
2328
+ const plainItems = addSignedUrlTokens(await Promise.all(items.map((item) => item.toClient())), config);
2326
2329
  return res.status(200).json({
2327
2330
  status: 200,
2328
2331
  message: "Trash items",
@@ -2377,14 +2380,14 @@ var driveAPIHandler = async (req, res) => {
2377
2380
  console.error(`Failed to move item ${id}`, e);
2378
2381
  }
2379
2382
  }
2380
- return res.status(200).json({ status: 200, message: "Moved", data: { items } });
2383
+ return res.status(200).json({ status: 200, message: "Moved", data: { items: addSignedUrlTokens(items, config) } });
2381
2384
  }
2382
2385
  // ** 8. RENAME **
2383
2386
  case "rename": {
2384
2387
  const renameData = renameBodySchema.safeParse({ id: req.query.id, ...req.body });
2385
2388
  if (!renameData.success) return res.status(400).json({ status: 400, message: "Invalid data" });
2386
2389
  const { id, newName } = renameData.data;
2387
- const item = await provider.rename(id, newName, owner, accountId);
2390
+ const item = addSignedUrlToken(await provider.rename(id, newName, owner, accountId), config);
2388
2391
  return res.status(200).json({ status: 200, message: "Renamed", data: { item } });
2389
2392
  }
2390
2393
  // ** 9. THUMBNAIL **
@@ -2398,5 +2401,5 @@ var driveAPIHandler = async (req, res) => {
2398
2401
  };
2399
2402
 
2400
2403
  export { driveAPIHandler, driveConfiguration, driveDelete, driveFilePath, driveFileSchemaZod, driveGetUrl, driveInfo, driveList, driveReadFile, driveUpload, getDriveConfig, getDriveInformation };
2401
- //# sourceMappingURL=chunk-SSOIUMFQ.js.map
2402
- //# sourceMappingURL=chunk-SSOIUMFQ.js.map
2404
+ //# sourceMappingURL=chunk-N3C7IKRK.js.map
2405
+ //# sourceMappingURL=chunk-N3C7IKRK.js.map