@muhgholy/next-drive 2.2.5 → 3.0.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.
@@ -1,7 +1,7 @@
1
1
  import { __require } from './chunk-DGUM43GV.js';
2
2
  import formidable from 'formidable';
3
3
  import path3 from 'path';
4
- import fs5 from 'fs';
4
+ import fs2 from 'fs';
5
5
  import os from 'os';
6
6
  import mongoose2, { Schema, isValidObjectId } from 'mongoose';
7
7
  import crypto2 from 'crypto';
@@ -126,18 +126,6 @@ StorageAccountSchema.method("toClient", async function() {
126
126
  });
127
127
  var StorageAccount = mongoose2.models.StorageAccount || mongoose2.model("StorageAccount", StorageAccountSchema);
128
128
  var account_default = StorageAccount;
129
- var moveFile = (src, dest) => {
130
- try {
131
- fs5.renameSync(src, dest);
132
- } catch (err) {
133
- if (err.code === "EXDEV") {
134
- fs5.copyFileSync(src, dest);
135
- fs5.unlinkSync(src);
136
- } else {
137
- throw err;
138
- }
139
- }
140
- };
141
129
  var validateMimeType = (mime, allowedTypes) => {
142
130
  if (allowedTypes.includes("*/*")) return true;
143
131
  return allowedTypes.some((pattern) => {
@@ -151,7 +139,7 @@ var validateMimeType = (mime, allowedTypes) => {
151
139
  };
152
140
  var computeFileHash = (filePath) => new Promise((resolve, reject) => {
153
141
  const hash = crypto2.createHash("sha256");
154
- const stream = fs5.createReadStream(filePath);
142
+ const stream = fs2.createReadStream(filePath);
155
143
  stream.on("data", (data) => hash.update(data));
156
144
  stream.on("end", () => resolve(hash.digest("hex")));
157
145
  stream.on("error", reject);
@@ -271,11 +259,11 @@ var LocalStorageProvider = {
271
259
  openStream: async (item, accountId) => {
272
260
  if (item.information.type !== "FILE") throw new Error("Cannot stream folder");
273
261
  const filePath = path3.join(getDriveConfig().storage.path, item.information.path);
274
- if (!fs5.existsSync(filePath)) {
262
+ if (!fs2.existsSync(filePath)) {
275
263
  throw new Error("File not found on disk");
276
264
  }
277
- const stat = fs5.statSync(filePath);
278
- const stream = fs5.createReadStream(filePath);
265
+ const stat = fs2.statSync(filePath);
266
+ const stream = fs2.createReadStream(filePath);
279
267
  return {
280
268
  stream,
281
269
  mime: item.information.mime,
@@ -287,11 +275,11 @@ var LocalStorageProvider = {
287
275
  const storagePath = getDriveConfig().storage.path;
288
276
  const originalPath = path3.join(storagePath, item.information.path);
289
277
  const thumbPath = path3.join(storagePath, "cache", "thumbnails", `${item._id.toString()}.webp`);
290
- if (!fs5.existsSync(originalPath)) throw new Error("Original file not found");
291
- if (fs5.existsSync(thumbPath)) {
292
- return fs5.createReadStream(thumbPath);
278
+ if (!fs2.existsSync(originalPath)) throw new Error("Original file not found");
279
+ if (fs2.existsSync(thumbPath)) {
280
+ return fs2.createReadStream(thumbPath);
293
281
  }
294
- if (!fs5.existsSync(path3.dirname(thumbPath))) fs5.mkdirSync(path3.dirname(thumbPath), { recursive: true });
282
+ if (!fs2.existsSync(path3.dirname(thumbPath))) fs2.mkdirSync(path3.dirname(thumbPath), { recursive: true });
295
283
  if (item.information.mime.startsWith("image/")) {
296
284
  await sharp(originalPath).resize(300, 300, { fit: "inside" }).toFormat("webp", { quality: 80 }).toFile(thumbPath);
297
285
  } else if (item.information.mime.startsWith("video/")) {
@@ -306,7 +294,7 @@ var LocalStorageProvider = {
306
294
  } else {
307
295
  throw new Error("Unsupported mime type for thumbnail");
308
296
  }
309
- return fs5.createReadStream(thumbPath);
297
+ return fs2.createReadStream(thumbPath);
310
298
  },
311
299
  createFolder: async (name, parentId, owner, accountId) => {
312
300
  const getNextOrderValue = async (owner2) => {
@@ -327,10 +315,33 @@ var LocalStorageProvider = {
327
315
  },
328
316
  uploadFile: async (drive, filePath, accountId) => {
329
317
  if (drive.information.type !== "FILE") throw new Error("Invalid drive type");
330
- const destPath = path3.join(getDriveConfig().storage.path, drive.information.path);
318
+ const storagePath = getDriveConfig().storage.path;
319
+ const destPath = path3.join(storagePath, drive.information.path);
331
320
  const dirPath = path3.dirname(destPath);
332
- if (!fs5.existsSync(dirPath)) fs5.mkdirSync(dirPath, { recursive: true });
333
- moveFile(filePath, destPath);
321
+ if (!fs2.existsSync(filePath)) {
322
+ throw new Error("Source file not found");
323
+ }
324
+ if (!fs2.existsSync(dirPath)) {
325
+ fs2.mkdirSync(dirPath, { recursive: true });
326
+ }
327
+ try {
328
+ fs2.renameSync(filePath, destPath);
329
+ } catch (err) {
330
+ if (err instanceof Error && "code" in err && err.code === "EXDEV") {
331
+ fs2.copyFileSync(filePath, destPath);
332
+ fs2.unlinkSync(filePath);
333
+ } else {
334
+ throw err;
335
+ }
336
+ }
337
+ if (!fs2.existsSync(destPath)) {
338
+ throw new Error("Failed to write file to destination");
339
+ }
340
+ const destStats = fs2.statSync(destPath);
341
+ if (destStats.size !== drive.information.sizeInBytes) {
342
+ fs2.unlinkSync(destPath);
343
+ throw new Error(`Destination file size mismatch: expected ${drive.information.sizeInBytes}, got ${destStats.size}`);
344
+ }
334
345
  drive.status = "READY";
335
346
  drive.information.hash = await computeFileHash(destPath);
336
347
  if (drive.information.mime.startsWith("image/")) {
@@ -359,8 +370,8 @@ var LocalStorageProvider = {
359
370
  if (item.information.type === "FILE" && item.information.path) {
360
371
  const fullPath = path3.join(getDriveConfig().storage.path, item.information.path);
361
372
  const dirPath = path3.dirname(fullPath);
362
- if (fs5.existsSync(dirPath)) {
363
- fs5.rmSync(dirPath, { recursive: true, force: true });
373
+ if (fs2.existsSync(dirPath)) {
374
+ fs2.rmSync(dirPath, { recursive: true, force: true });
364
375
  }
365
376
  }
366
377
  }
@@ -669,7 +680,7 @@ var GoogleDriveProvider = {
669
680
  },
670
681
  media: {
671
682
  mimeType: drive.information.mime,
672
- body: fs5.createReadStream(filePath)
683
+ body: fs2.createReadStream(filePath)
673
684
  },
674
685
  fields: "id, name, mimeType, webViewLink, iconLink, thumbnailLink, size"
675
686
  });
@@ -859,7 +870,7 @@ var driveFilePath = async (file) => {
859
870
  const providerType = drive.provider?.type || "LOCAL";
860
871
  if (providerType === "LOCAL") {
861
872
  const filePath = path3.join(STORAGE_PATH, drive.information.path);
862
- if (!fs5.existsSync(filePath)) {
873
+ if (!fs2.existsSync(filePath)) {
863
874
  throw new Error(`Local file not found on disk: ${filePath}`);
864
875
  }
865
876
  return Object.freeze({
@@ -874,8 +885,8 @@ var driveFilePath = async (file) => {
874
885
  const libraryDir = path3.join(STORAGE_PATH, "library", "google");
875
886
  const fileName = `${drive._id}${path3.extname(drive.name)}`;
876
887
  const cachedFilePath = path3.join(libraryDir, fileName);
877
- if (fs5.existsSync(cachedFilePath)) {
878
- const stats = fs5.statSync(cachedFilePath);
888
+ if (fs2.existsSync(cachedFilePath)) {
889
+ const stats = fs2.statSync(cachedFilePath);
879
890
  if (stats.size === drive.information.sizeInBytes) {
880
891
  return Object.freeze({
881
892
  path: cachedFilePath,
@@ -885,22 +896,31 @@ var driveFilePath = async (file) => {
885
896
  provider: "GOOGLE"
886
897
  });
887
898
  }
888
- fs5.unlinkSync(cachedFilePath);
899
+ fs2.unlinkSync(cachedFilePath);
889
900
  }
890
901
  const accountId = drive.storageAccountId?.toString();
891
902
  const { stream } = await GoogleDriveProvider.openStream(drive, accountId);
892
- if (!fs5.existsSync(libraryDir)) {
893
- fs5.mkdirSync(libraryDir, { recursive: true });
903
+ if (!fs2.existsSync(libraryDir)) {
904
+ fs2.mkdirSync(libraryDir, { recursive: true });
894
905
  }
895
906
  const tempPath = `${cachedFilePath}.tmp`;
896
- const writeStream = fs5.createWriteStream(tempPath);
907
+ const writeStream = fs2.createWriteStream(tempPath);
897
908
  await new Promise((resolve, reject) => {
898
909
  stream.pipe(writeStream);
899
910
  writeStream.on("finish", resolve);
900
911
  writeStream.on("error", reject);
901
912
  stream.on("error", reject);
902
913
  });
903
- moveFile(tempPath, cachedFilePath);
914
+ try {
915
+ fs2.renameSync(tempPath, cachedFilePath);
916
+ } catch (err) {
917
+ if (err instanceof Error && "code" in err && err.code === "EXDEV") {
918
+ fs2.copyFileSync(tempPath, cachedFilePath);
919
+ fs2.unlinkSync(tempPath);
920
+ } else {
921
+ throw err;
922
+ }
923
+ }
904
924
  return Object.freeze({
905
925
  path: cachedFilePath,
906
926
  name: drive.name,
@@ -1173,7 +1193,7 @@ var driveAPIHandler = async (req, res) => {
1173
1193
  case "upload": {
1174
1194
  if (req.method !== "POST") return res.status(405).json({ status: 405, message: "Only POST allowed" });
1175
1195
  const systemTmpDir = path3.join(os.tmpdir(), "next-drive-uploads");
1176
- if (!fs5.existsSync(systemTmpDir)) fs5.mkdirSync(systemTmpDir, { recursive: true });
1196
+ if (!fs2.existsSync(systemTmpDir)) fs2.mkdirSync(systemTmpDir, { recursive: true });
1177
1197
  const form = formidable({
1178
1198
  multiples: false,
1179
1199
  maxFileSize: config.security.maxUploadSizeInBytes * 2,
@@ -1188,7 +1208,7 @@ var driveAPIHandler = async (req, res) => {
1188
1208
  });
1189
1209
  const cleanupTempFiles = (files2) => {
1190
1210
  Object.values(files2).flat().forEach((file) => {
1191
- if (file && fs5.existsSync(file.filepath)) fs5.rmSync(file.filepath, { force: true });
1211
+ if (file && fs2.existsSync(file.filepath)) fs2.rmSync(file.filepath, { force: true });
1192
1212
  });
1193
1213
  };
1194
1214
  const getString = (f) => Array.isArray(f) ? f[0] : f || "";
@@ -1222,7 +1242,7 @@ var driveAPIHandler = async (req, res) => {
1222
1242
  }
1223
1243
  currentUploadId = crypto.randomUUID();
1224
1244
  const uploadDir = path3.join(tempBaseDir, currentUploadId);
1225
- fs5.mkdirSync(uploadDir, { recursive: true });
1245
+ fs2.mkdirSync(uploadDir, { recursive: true });
1226
1246
  const metadata = {
1227
1247
  owner,
1228
1248
  accountId,
@@ -1233,11 +1253,11 @@ var driveAPIHandler = async (req, res) => {
1233
1253
  mimeType: fileType,
1234
1254
  totalChunks
1235
1255
  };
1236
- fs5.writeFileSync(path3.join(uploadDir, "metadata.json"), JSON.stringify(metadata));
1256
+ fs2.writeFileSync(path3.join(uploadDir, "metadata.json"), JSON.stringify(metadata));
1237
1257
  }
1238
1258
  if (currentUploadId) {
1239
1259
  const uploadDir = path3.join(tempBaseDir, currentUploadId);
1240
- if (!fs5.existsSync(uploadDir)) {
1260
+ if (!fs2.existsSync(uploadDir)) {
1241
1261
  cleanupTempFiles(files);
1242
1262
  return res.status(404).json({ status: 404, message: "Upload session not found or expired" });
1243
1263
  }
@@ -1245,23 +1265,47 @@ var driveAPIHandler = async (req, res) => {
1245
1265
  const chunkFile = Array.isArray(files.chunk) ? files.chunk[0] : files.chunk;
1246
1266
  if (!chunkFile) throw new Error("No chunk file received");
1247
1267
  const partPath = path3.join(uploadDir, `part_${chunkIndex}`);
1248
- moveFile(chunkFile.filepath, partPath);
1249
- const uploadedParts = fs5.readdirSync(uploadDir).filter((f) => f.startsWith("part_"));
1268
+ try {
1269
+ fs2.renameSync(chunkFile.filepath, partPath);
1270
+ } catch (err) {
1271
+ if (err instanceof Error && "code" in err && err.code === "EXDEV") {
1272
+ fs2.copyFileSync(chunkFile.filepath, partPath);
1273
+ fs2.unlinkSync(chunkFile.filepath);
1274
+ } else {
1275
+ throw err;
1276
+ }
1277
+ }
1278
+ const uploadedParts = fs2.readdirSync(uploadDir).filter((f) => f.startsWith("part_"));
1250
1279
  if (uploadedParts.length === totalChunks) {
1251
1280
  const metaPath = path3.join(uploadDir, "metadata.json");
1252
- const meta = JSON.parse(fs5.readFileSync(metaPath, "utf-8"));
1281
+ const meta = JSON.parse(fs2.readFileSync(metaPath, "utf-8"));
1253
1282
  const finalTempPath = path3.join(uploadDir, "final.bin");
1254
- const writeStream = fs5.createWriteStream(finalTempPath);
1283
+ const writeStream = fs2.createWriteStream(finalTempPath);
1284
+ await new Promise((resolve, reject) => {
1285
+ writeStream.on("open", () => resolve());
1286
+ writeStream.on("error", reject);
1287
+ });
1255
1288
  for (let i = 0; i < totalChunks; i++) {
1256
1289
  const pPath = path3.join(uploadDir, `part_${i}`);
1257
- const data = fs5.readFileSync(pPath);
1290
+ if (!fs2.existsSync(pPath)) {
1291
+ writeStream.destroy();
1292
+ throw new Error(`Missing chunk part: ${i}`);
1293
+ }
1294
+ const data = fs2.readFileSync(pPath);
1258
1295
  writeStream.write(data);
1259
1296
  }
1260
1297
  await new Promise((resolve, reject) => {
1298
+ writeStream.end();
1261
1299
  writeStream.on("finish", resolve);
1262
1300
  writeStream.on("error", reject);
1263
- writeStream.end();
1264
1301
  });
1302
+ if (!fs2.existsSync(finalTempPath)) {
1303
+ throw new Error("Failed to create merged file");
1304
+ }
1305
+ const finalStats = fs2.statSync(finalTempPath);
1306
+ if (finalStats.size !== meta.fileSize) {
1307
+ throw new Error(`File size mismatch: expected ${meta.fileSize}, got ${finalStats.size}`);
1308
+ }
1265
1309
  const drive = new drive_default({
1266
1310
  owner: meta.owner,
1267
1311
  storageAccountId: meta.accountId || null,
@@ -1284,7 +1328,7 @@ var driveAPIHandler = async (req, res) => {
1284
1328
  await drive.save();
1285
1329
  try {
1286
1330
  const item = await provider.uploadFile(drive, finalTempPath, meta.accountId);
1287
- fs5.rmSync(uploadDir, { recursive: true, force: true });
1331
+ fs2.rmSync(uploadDir, { recursive: true, force: true });
1288
1332
  const newQuota = await provider.getQuota(meta.owner, meta.accountId, information.storage.quotaInBytes);
1289
1333
  res.status(200).json({ status: 200, message: "Upload complete", data: { type: "UPLOAD_COMPLETE", driveId: String(drive._id), item }, statistic: { storage: newQuota } });
1290
1334
  } catch (err) {
@@ -1314,9 +1358,9 @@ var driveAPIHandler = async (req, res) => {
1314
1358
  if (!cancelData.success) return res.status(400).json({ status: 400, message: "Invalid ID" });
1315
1359
  const { id } = cancelData.data;
1316
1360
  const tempUploadDir = path3.join(os.tmpdir(), "next-drive-uploads", id);
1317
- if (fs5.existsSync(tempUploadDir)) {
1361
+ if (fs2.existsSync(tempUploadDir)) {
1318
1362
  try {
1319
- fs5.rmSync(tempUploadDir, { recursive: true, force: true });
1363
+ fs2.rmSync(tempUploadDir, { recursive: true, force: true });
1320
1364
  } catch (e) {
1321
1365
  console.error("Failed to cleanup temp upload:", e);
1322
1366
  }
@@ -1495,5 +1539,5 @@ var driveAPIHandler = async (req, res) => {
1495
1539
  };
1496
1540
 
1497
1541
  export { driveAPIHandler, driveConfiguration, driveFilePath, driveFileSchemaZod, driveGetUrl, driveReadFile, getDriveConfig, getDriveInformation };
1498
- //# sourceMappingURL=chunk-EV3BVXQN.js.map
1499
- //# sourceMappingURL=chunk-EV3BVXQN.js.map
1542
+ //# sourceMappingURL=chunk-KCDI2FBD.js.map
1543
+ //# sourceMappingURL=chunk-KCDI2FBD.js.map