@muhgholy/next-drive 3.7.0 → 3.7.1

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
@@ -286,13 +286,13 @@ const file = await driveUpload(
286
286
 
287
287
  **Options:**
288
288
 
289
- | Option | Type | Required | Description |
290
- | ----------- | ---------------- | -------- | ---------------------------------------------- |
291
- | `name` | `string` | Yes | File name with extension |
292
- | `parentId` | `string \| null` | No | Parent folder ID (null or 'root' for root) |
293
- | `accountId` | `string` | No | Storage account ID ('LOCAL' for local storage) |
289
+ | Option | Type | Required | Description |
290
+ | ----------- | ---------------- | -------- | -------------------------------------------------------- |
291
+ | `name` | `string` | Yes | File name with extension |
292
+ | `parentId` | `string \| null` | No | Parent folder ID (null or 'root' for root) |
293
+ | `accountId` | `string` | No | Storage account ID ('LOCAL' for local storage) |
294
294
  | `mime` | `string` | No | MIME type (auto-detected from extension if not provided) |
295
- | `enforce` | `boolean` | No | Bypass quota check (default: false) |
295
+ | `enforce` | `boolean` | No | Bypass quota check (default: false) |
296
296
 
297
297
  ### Get Signed URL
298
298
 
@@ -5,8 +5,8 @@ var formidable = require('formidable');
5
5
  var path3 = require('path');
6
6
  var fs4 = require('fs');
7
7
  var os2 = require('os');
8
+ var crypto2 = require('crypto');
8
9
  var mongoose2 = require('mongoose');
9
- var crypto3 = require('crypto');
10
10
  var sharp = require('sharp');
11
11
  var zod = require('zod');
12
12
  var ffmpeg = require('fluent-ffmpeg');
@@ -18,8 +18,8 @@ var formidable__default = /*#__PURE__*/_interopDefault(formidable);
18
18
  var path3__default = /*#__PURE__*/_interopDefault(path3);
19
19
  var fs4__default = /*#__PURE__*/_interopDefault(fs4);
20
20
  var os2__default = /*#__PURE__*/_interopDefault(os2);
21
+ var crypto2__default = /*#__PURE__*/_interopDefault(crypto2);
21
22
  var mongoose2__default = /*#__PURE__*/_interopDefault(mongoose2);
22
- var crypto3__default = /*#__PURE__*/_interopDefault(crypto3);
23
23
  var sharp__default = /*#__PURE__*/_interopDefault(sharp);
24
24
  var ffmpeg__default = /*#__PURE__*/_interopDefault(ffmpeg);
25
25
 
@@ -151,7 +151,7 @@ var validateMimeType = (mime, allowedTypes) => {
151
151
  });
152
152
  };
153
153
  var computeFileHash = (filePath) => new Promise((resolve, reject) => {
154
- const hash = crypto3__default.default.createHash("sha256");
154
+ const hash = crypto2__default.default.createHash("sha256");
155
155
  const stream = fs4__default.default.createReadStream(filePath);
156
156
  stream.on("data", (data) => hash.update(data));
157
157
  stream.on("end", () => resolve(hash.digest("hex")));
@@ -194,13 +194,13 @@ var listQuerySchema = zod.z.object({
194
194
  }),
195
195
  afterId: objectIdSchema.optional()
196
196
  });
197
- var serveQuerySchema = zod.z.object({
197
+ zod.z.object({
198
198
  id: objectIdSchema,
199
199
  token: zod.z.string().optional(),
200
200
  q: zod.z.enum(["ultralow", "low", "medium", "high", "normal"]).optional(),
201
201
  format: zod.z.enum(["webp", "jpeg", "png"]).optional()
202
202
  });
203
- var thumbnailQuerySchema = zod.z.object({
203
+ zod.z.object({
204
204
  id: objectIdSchema,
205
205
  size: zod.z.enum(["small", "medium", "large"]).optional().default("medium"),
206
206
  token: zod.z.string().optional()
@@ -857,7 +857,7 @@ var driveGetUrl = (fileId, options) => {
857
857
  } else {
858
858
  expiryTimestamp = Math.floor(Date.now() / 1e3) + expiresIn;
859
859
  }
860
- const signature = crypto3__default.default.createHmac("sha256", secret).update(`${fileId}:${expiryTimestamp}`).digest("hex");
860
+ const signature = crypto2__default.default.createHmac("sha256", secret).update(`${fileId}:${expiryTimestamp}`).digest("hex");
861
861
  const token = Buffer.from(`${expiryTimestamp}:${signature}`).toString("base64url");
862
862
  return `/api/drive?action=serve&id=${fileId}&token=${token}`;
863
863
  };
@@ -1084,7 +1084,7 @@ var driveUpload = async (source, key, options) => {
1084
1084
  if (!fs4__default.default.existsSync(tempDir)) {
1085
1085
  fs4__default.default.mkdirSync(tempDir, { recursive: true });
1086
1086
  }
1087
- tempFilePath = path3__default.default.join(tempDir, `upload-${crypto3__default.default.randomUUID()}.tmp`);
1087
+ tempFilePath = path3__default.default.join(tempDir, `upload-${crypto2__default.default.randomUUID()}.tmp`);
1088
1088
  fs4__default.default.writeFileSync(tempFilePath, source);
1089
1089
  sourceFilePath = tempFilePath;
1090
1090
  fileSize = source.length;
@@ -1093,7 +1093,7 @@ var driveUpload = async (source, key, options) => {
1093
1093
  if (!fs4__default.default.existsSync(tempDir)) {
1094
1094
  fs4__default.default.mkdirSync(tempDir, { recursive: true });
1095
1095
  }
1096
- tempFilePath = path3__default.default.join(tempDir, `upload-${crypto3__default.default.randomUUID()}.tmp`);
1096
+ tempFilePath = path3__default.default.join(tempDir, `upload-${crypto2__default.default.randomUUID()}.tmp`);
1097
1097
  const writeStream = fs4__default.default.createWriteStream(tempFilePath);
1098
1098
  await new Promise((resolve, reject) => {
1099
1099
  source.pipe(writeStream);
@@ -1256,6 +1256,65 @@ var driveAPIHandler = async (req, res) => {
1256
1256
  res.status(400).json({ status: 400, message: "Missing action query parameter" });
1257
1257
  return;
1258
1258
  }
1259
+ if (action === "serve" || action === "thumbnail") {
1260
+ try {
1261
+ const { id, token } = req.query;
1262
+ if (!id || typeof id !== "string") {
1263
+ return res.status(400).json({ status: 400, message: "Missing or invalid file ID" });
1264
+ }
1265
+ const drive = await drive_default.findById(id);
1266
+ if (!drive) return res.status(404).json({ status: 404, message: "File not found" });
1267
+ if (config.security.signedUrls?.enabled) {
1268
+ if (!token || typeof token !== "string") {
1269
+ return res.status(401).json({ status: 401, message: "Missing or invalid token" });
1270
+ }
1271
+ try {
1272
+ const decoded = Buffer.from(token, "base64url").toString();
1273
+ const [expiryStr, signature] = decoded.split(":");
1274
+ const expiry = parseInt(expiryStr, 10);
1275
+ if (Date.now() / 1e3 > expiry) {
1276
+ return res.status(401).json({ status: 401, message: "Token expired" });
1277
+ }
1278
+ const { secret } = config.security.signedUrls;
1279
+ const expectedSignature = crypto2__default.default.createHmac("sha256", secret).update(`${id}:${expiry}`).digest("hex");
1280
+ if (signature !== expectedSignature) {
1281
+ return res.status(401).json({ status: 401, message: "Invalid token" });
1282
+ }
1283
+ } catch (err) {
1284
+ return res.status(401).json({ status: 401, message: "Invalid token format" });
1285
+ }
1286
+ }
1287
+ const itemProvider = drive.provider?.type === "GOOGLE" ? GoogleDriveProvider : LocalStorageProvider;
1288
+ const itemAccountId = drive.storageAccountId ? drive.storageAccountId.toString() : void 0;
1289
+ if (action === "thumbnail") {
1290
+ const stream = await itemProvider.getThumbnail(drive, itemAccountId);
1291
+ res.setHeader("Content-Type", "image/webp");
1292
+ if (config.cors?.enabled) {
1293
+ res.setHeader("Cross-Origin-Resource-Policy", "cross-origin");
1294
+ }
1295
+ stream.pipe(res);
1296
+ return;
1297
+ }
1298
+ if (action === "serve") {
1299
+ const { stream, mime, size } = await itemProvider.openStream(drive, itemAccountId);
1300
+ const safeFilename = sanitizeContentDispositionFilename(drive.name);
1301
+ res.setHeader("Content-Disposition", `inline; filename="${safeFilename}"`);
1302
+ res.setHeader("Content-Type", mime);
1303
+ if (config.cors?.enabled) {
1304
+ res.setHeader("Cross-Origin-Resource-Policy", "cross-origin");
1305
+ }
1306
+ if (size) res.setHeader("Content-Length", size);
1307
+ stream.pipe(res);
1308
+ return;
1309
+ }
1310
+ } catch (error) {
1311
+ console.error(`[next-drive] Error in ${action}:`, error);
1312
+ return res.status(500).json({
1313
+ status: 500,
1314
+ message: error instanceof Error ? error.message : "Unknown error"
1315
+ });
1316
+ }
1317
+ }
1259
1318
  try {
1260
1319
  const information = await getDriveInformation(req);
1261
1320
  const { key: owner } = information;
@@ -1490,7 +1549,7 @@ var driveAPIHandler = async (req, res) => {
1490
1549
  cleanupTempFiles(files);
1491
1550
  return res.status(413).json({ status: 413, message: "Storage quota exceeded" });
1492
1551
  }
1493
- currentUploadId = crypto.randomUUID();
1552
+ currentUploadId = crypto2__default.default.randomUUID();
1494
1553
  const uploadDir = path3__default.default.join(tempBaseDir, currentUploadId);
1495
1554
  fs4__default.default.mkdirSync(uploadDir, { recursive: true });
1496
1555
  const metadata = {
@@ -1743,42 +1802,6 @@ var driveAPIHandler = async (req, res) => {
1743
1802
  return res.status(200).json({ status: 200, message: "Renamed", data: { item } });
1744
1803
  }
1745
1804
  // ** 9. THUMBNAIL **
1746
- case "thumbnail": {
1747
- const thumbQuery = thumbnailQuerySchema.safeParse(req.query);
1748
- if (!thumbQuery.success) return res.status(400).json({ status: 400, message: "Invalid params" });
1749
- const { id } = thumbQuery.data;
1750
- const drive = await drive_default.findById(id);
1751
- if (!drive) return res.status(404).json({ status: 404, message: "Not found" });
1752
- const itemProvider = drive.provider?.type === "GOOGLE" ? GoogleDriveProvider : LocalStorageProvider;
1753
- const itemAccountId = drive.storageAccountId ? drive.storageAccountId.toString() : void 0;
1754
- const stream = await itemProvider.getThumbnail(drive, itemAccountId);
1755
- res.setHeader("Content-Type", "image/webp");
1756
- if (config.cors?.enabled) {
1757
- res.setHeader("Cross-Origin-Resource-Policy", "cross-origin");
1758
- }
1759
- stream.pipe(res);
1760
- return;
1761
- }
1762
- // ** 10. SERVE / DOWNLOAD **
1763
- case "serve": {
1764
- const serveQuery = serveQuerySchema.safeParse(req.query);
1765
- if (!serveQuery.success) return res.status(400).json({ status: 400, message: "Invalid params" });
1766
- const { id } = serveQuery.data;
1767
- const drive = await drive_default.findById(id);
1768
- if (!drive) return res.status(404).json({ status: 404, message: "Not found" });
1769
- const itemProvider = drive.provider?.type === "GOOGLE" ? GoogleDriveProvider : LocalStorageProvider;
1770
- const itemAccountId = drive.storageAccountId ? drive.storageAccountId.toString() : void 0;
1771
- const { stream, mime, size } = await itemProvider.openStream(drive, itemAccountId);
1772
- const safeFilename = sanitizeContentDispositionFilename(drive.name);
1773
- res.setHeader("Content-Disposition", `inline; filename="${safeFilename}"`);
1774
- res.setHeader("Content-Type", mime);
1775
- if (config.cors?.enabled) {
1776
- res.setHeader("Cross-Origin-Resource-Policy", "cross-origin");
1777
- }
1778
- if (size) res.setHeader("Content-Length", size);
1779
- stream.pipe(res);
1780
- return;
1781
- }
1782
1805
  default:
1783
1806
  res.status(400).json({ status: 400, message: `Unknown action: ${action}` });
1784
1807
  }
@@ -1800,5 +1823,5 @@ exports.driveReadFile = driveReadFile;
1800
1823
  exports.driveUpload = driveUpload;
1801
1824
  exports.getDriveConfig = getDriveConfig;
1802
1825
  exports.getDriveInformation = getDriveInformation;
1803
- //# sourceMappingURL=chunk-7PTOIRBL.cjs.map
1804
- //# sourceMappingURL=chunk-7PTOIRBL.cjs.map
1826
+ //# sourceMappingURL=chunk-LMM5IU5U.cjs.map
1827
+ //# sourceMappingURL=chunk-LMM5IU5U.cjs.map