@muhgholy/next-drive 4.23.33 → 4.23.34
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 +79 -3
- package/dist/{chunk-HWNIRA6Y.cjs → chunk-DHIHCER6.cjs} +220 -71
- package/dist/chunk-DHIHCER6.cjs.map +1 -0
- package/dist/{chunk-2DCZENJ2.js → chunk-E72MTT6W.js} +3 -3
- package/dist/{chunk-2DCZENJ2.js.map → chunk-E72MTT6W.js.map} +1 -1
- package/dist/{chunk-WUG4ATLH.cjs → chunk-PKNXKHGS.cjs} +37 -37
- package/dist/{chunk-WUG4ATLH.cjs.map → chunk-PKNXKHGS.cjs.map} +1 -1
- package/dist/{chunk-IWV6G7HQ.js → chunk-QMPV4YSZ.js} +221 -73
- package/dist/chunk-QMPV4YSZ.js.map +1 -0
- package/dist/server/controllers/drive.d.ts +33 -2
- package/dist/server/controllers/drive.d.ts.map +1 -1
- package/dist/server/errors.d.ts +12 -0
- package/dist/server/errors.d.ts.map +1 -0
- package/dist/server/express.cjs +16 -12
- package/dist/server/express.cjs.map +1 -1
- package/dist/server/express.d.ts +3 -0
- package/dist/server/express.d.ts.map +1 -1
- package/dist/server/express.js +4 -4
- package/dist/server/express.js.map +1 -1
- package/dist/server/hono.cjs +16 -12
- package/dist/server/hono.cjs.map +1 -1
- package/dist/server/hono.d.ts +3 -0
- package/dist/server/hono.d.ts.map +1 -1
- package/dist/server/hono.js +4 -4
- package/dist/server/hono.js.map +1 -1
- package/dist/server/index.cjs +23 -19
- package/dist/server/index.d.ts +3 -0
- package/dist/server/index.d.ts.map +1 -1
- package/dist/server/index.js +2 -2
- package/dist/server/storage-adapters/google.d.ts.map +1 -1
- package/dist/server/tus.d.ts.map +1 -1
- package/dist/tus-EQ2IXSRE.cjs +20 -0
- package/dist/tus-EQ2IXSRE.cjs.map +1 -0
- package/dist/tus-ODCMFEBZ.js +3 -0
- package/dist/tus-ODCMFEBZ.js.map +1 -0
- package/dist/types/server/storage.d.ts +4 -1
- package/dist/types/server/storage.d.ts.map +1 -1
- package/package.json +1 -1
- package/dist/chunk-HWNIRA6Y.cjs.map +0 -1
- package/dist/chunk-IWV6G7HQ.js.map +0 -1
- package/dist/tus-5LLFMVQK.cjs +0 -20
- package/dist/tus-5LLFMVQK.cjs.map +0 -1
- package/dist/tus-C4T5ZDTG.js +0 -3
- package/dist/tus-C4T5ZDTG.js.map +0 -1
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import fs from 'fs';
|
|
2
2
|
import path from 'path';
|
|
3
|
-
import { Readable } from 'stream';
|
|
3
|
+
import { PassThrough, Readable } from 'stream';
|
|
4
4
|
import mongoose, { Schema, isValidObjectId } from 'mongoose';
|
|
5
5
|
import { Server, EVENTS } from '@tus/server';
|
|
6
6
|
import { FileStore } from '@tus/file-store';
|
|
@@ -505,6 +505,15 @@ var getImageSettings = (fileSizeInBytes, qualityPreset, display, size, fit, posi
|
|
|
505
505
|
...resolvedPosition && { position: resolvedPosition }
|
|
506
506
|
};
|
|
507
507
|
};
|
|
508
|
+
|
|
509
|
+
// src/server/errors.ts
|
|
510
|
+
var DriveError = class extends Error {
|
|
511
|
+
constructor(code, message) {
|
|
512
|
+
super(message);
|
|
513
|
+
this.name = "DriveError";
|
|
514
|
+
this.code = code;
|
|
515
|
+
}
|
|
516
|
+
};
|
|
508
517
|
var generatePlaceholderThumbnail = async (outputPath, mimeType) => {
|
|
509
518
|
const typeParts = mimeType.split("/");
|
|
510
519
|
const subtype = typeParts[1] || "file";
|
|
@@ -961,7 +970,7 @@ var GoogleDriveProvider = {
|
|
|
961
970
|
return { usedInBytes: 0, quotaInBytes: 0 };
|
|
962
971
|
}
|
|
963
972
|
},
|
|
964
|
-
openStream: async (item, accountId) => {
|
|
973
|
+
openStream: async (item, accountId, options) => {
|
|
965
974
|
if (item.information.type === "FOLDER") throw new Error("Could not open Google Drive file: folders cannot be streamed");
|
|
966
975
|
const cachePath = path.join(getDriveConfig().storage.path, "file", item._id.toString(), "data.bin");
|
|
967
976
|
if (fs.existsSync(cachePath)) {
|
|
@@ -978,24 +987,36 @@ var GoogleDriveProvider = {
|
|
|
978
987
|
{ fileId: item.provider.google.id, alt: "media" },
|
|
979
988
|
{ responseType: "stream" }
|
|
980
989
|
);
|
|
981
|
-
|
|
982
|
-
if (!
|
|
983
|
-
|
|
984
|
-
|
|
985
|
-
|
|
986
|
-
|
|
987
|
-
|
|
988
|
-
|
|
989
|
-
|
|
990
|
-
|
|
991
|
-
|
|
992
|
-
|
|
993
|
-
|
|
994
|
-
|
|
990
|
+
let cacheWritePromise;
|
|
991
|
+
if (!options?.skipCache) {
|
|
992
|
+
const fileDir = path.dirname(cachePath);
|
|
993
|
+
if (!fs.existsSync(fileDir)) fs.mkdirSync(fileDir, { recursive: true });
|
|
994
|
+
const tempPath = `${cachePath}.tmp`;
|
|
995
|
+
const writeStream = fs.createWriteStream(tempPath);
|
|
996
|
+
res.data.pipe(writeStream);
|
|
997
|
+
cacheWritePromise = new Promise((resolve, reject) => {
|
|
998
|
+
writeStream.on("finish", () => {
|
|
999
|
+
try {
|
|
1000
|
+
fs.renameSync(tempPath, cachePath);
|
|
1001
|
+
resolve();
|
|
1002
|
+
} catch {
|
|
1003
|
+
fs.unlink(tempPath, () => {
|
|
1004
|
+
});
|
|
1005
|
+
reject(new Error("Could not cache Google Drive file: failed to finalize the cached file"));
|
|
1006
|
+
}
|
|
1007
|
+
});
|
|
1008
|
+
writeStream.on("error", (err) => {
|
|
1009
|
+
fs.unlink(tempPath, () => {
|
|
1010
|
+
});
|
|
1011
|
+
reject(err);
|
|
1012
|
+
});
|
|
1013
|
+
});
|
|
1014
|
+
}
|
|
995
1015
|
return {
|
|
996
1016
|
stream: res.data,
|
|
997
1017
|
mime: item.information.mime,
|
|
998
|
-
size: item.information.sizeInBytes
|
|
1018
|
+
size: item.information.sizeInBytes,
|
|
1019
|
+
cacheWritePromise
|
|
999
1020
|
};
|
|
1000
1021
|
},
|
|
1001
1022
|
getThumbnail: async (item, accountId) => {
|
|
@@ -1259,28 +1280,44 @@ var driveAddSignedUrlToken = (item, config) => {
|
|
|
1259
1280
|
var driveAddSignedUrlTokens = (items, config) => {
|
|
1260
1281
|
return items.map((item) => driveAddSignedUrlToken(item, config));
|
|
1261
1282
|
};
|
|
1262
|
-
var driveReadFile = async (file) => {
|
|
1283
|
+
var driveReadFile = async (file, options) => {
|
|
1263
1284
|
let drive;
|
|
1264
1285
|
if (typeof file === "string") {
|
|
1265
1286
|
const doc = await drive_default.findById(file);
|
|
1266
|
-
if (!doc) throw new
|
|
1287
|
+
if (!doc) throw new DriveError("NOT_FOUND", "Could not read file: the file no longer exists");
|
|
1267
1288
|
drive = doc;
|
|
1268
1289
|
} else if ("toClient" in file) {
|
|
1269
1290
|
drive = file;
|
|
1270
1291
|
} else {
|
|
1271
|
-
throw new
|
|
1292
|
+
throw new DriveError("INVALID_REFERENCE", "Could not read file: invalid file reference provided");
|
|
1272
1293
|
}
|
|
1273
1294
|
if (drive.information.type !== "FILE") {
|
|
1274
|
-
throw new
|
|
1295
|
+
throw new DriveError("INVALID_TYPE", "Could not read file: this item is a folder, not a file");
|
|
1275
1296
|
}
|
|
1276
1297
|
const provider = drive.provider?.type === "GOOGLE" ? GoogleDriveProvider : LocalStorageProvider;
|
|
1277
1298
|
const accountId = drive.storageAccountId?.toString();
|
|
1278
|
-
|
|
1299
|
+
const driveId = String(drive._id);
|
|
1300
|
+
let result;
|
|
1301
|
+
if (provider === GoogleDriveProvider && cacheWriteLocks.has(driveId)) {
|
|
1302
|
+
result = await provider.openStream(drive, accountId, { skipCache: true });
|
|
1303
|
+
} else {
|
|
1304
|
+
result = await provider.openStream(drive, accountId);
|
|
1305
|
+
if (result.cacheWritePromise) {
|
|
1306
|
+
const cacheWritePromise = result.cacheWritePromise;
|
|
1307
|
+
cacheWritePromise.catch(() => {
|
|
1308
|
+
});
|
|
1309
|
+
cacheWriteLocks.set(driveId, cacheWritePromise);
|
|
1310
|
+
cacheWritePromise.finally(() => {
|
|
1311
|
+
if (cacheWriteLocks.get(driveId) === cacheWritePromise) cacheWriteLocks.delete(driveId);
|
|
1312
|
+
});
|
|
1313
|
+
}
|
|
1314
|
+
}
|
|
1315
|
+
return { ...result, stream: instrumentReadableForProgress(result.stream, result.size, options) };
|
|
1279
1316
|
};
|
|
1280
1317
|
var driveInfo = async (source) => {
|
|
1281
1318
|
const fileId = typeof source === "string" ? source : source.id;
|
|
1282
1319
|
const drive = await drive_default.findById(fileId);
|
|
1283
|
-
if (!drive) throw new
|
|
1320
|
+
if (!drive) throw new DriveError("NOT_FOUND", "Could not load file details: the file no longer exists");
|
|
1284
1321
|
let parentName;
|
|
1285
1322
|
if (drive.parentId) {
|
|
1286
1323
|
const parent = await drive_default.findById(drive.parentId);
|
|
@@ -1315,19 +1352,107 @@ var driveInfo = async (source) => {
|
|
|
1315
1352
|
}
|
|
1316
1353
|
return info;
|
|
1317
1354
|
};
|
|
1318
|
-
var
|
|
1355
|
+
var computeTransferPercentage = (bytesDownloaded, totalBytes) => {
|
|
1356
|
+
if (!totalBytes) return 0;
|
|
1357
|
+
return Math.min(100, Math.round(bytesDownloaded / totalBytes * 100));
|
|
1358
|
+
};
|
|
1359
|
+
var cacheWriteLocks = /* @__PURE__ */ new Map();
|
|
1360
|
+
var inFlightGoogleDownloads = /* @__PURE__ */ new Map();
|
|
1361
|
+
var downloadGoogleFileToCache = async (drive, accountId, cachePath, totalBytes, listeners, controller) => {
|
|
1362
|
+
if (controller.signal.aborted) {
|
|
1363
|
+
throw new DriveError("ABORTED", "Could not download file: the download was aborted");
|
|
1364
|
+
}
|
|
1365
|
+
const { stream } = await GoogleDriveProvider.openStream(drive, accountId, { skipCache: true });
|
|
1366
|
+
if (controller.signal.aborted) {
|
|
1367
|
+
stream.destroy();
|
|
1368
|
+
throw new DriveError("ABORTED", "Could not download file: the download was aborted");
|
|
1369
|
+
}
|
|
1370
|
+
const fileDir = path.dirname(cachePath);
|
|
1371
|
+
if (!fs.existsSync(fileDir)) fs.mkdirSync(fileDir, { recursive: true });
|
|
1372
|
+
const tempPath = `${cachePath}.tmp`;
|
|
1373
|
+
const writeStream = fs.createWriteStream(tempPath);
|
|
1374
|
+
const cleanupTempFile = () => fs.unlink(tempPath, () => {
|
|
1375
|
+
});
|
|
1376
|
+
const onAbort = () => {
|
|
1377
|
+
const abortError = new DriveError("ABORTED", "Could not download file: the download was aborted");
|
|
1378
|
+
stream.destroy(abortError);
|
|
1379
|
+
writeStream.destroy(abortError);
|
|
1380
|
+
};
|
|
1381
|
+
controller.signal.addEventListener("abort", onAbort, { once: true });
|
|
1382
|
+
try {
|
|
1383
|
+
let bytesDownloaded = 0;
|
|
1384
|
+
await new Promise((resolve, reject) => {
|
|
1385
|
+
stream.on("data", (chunk) => {
|
|
1386
|
+
bytesDownloaded += chunk.length;
|
|
1387
|
+
const info = {
|
|
1388
|
+
bytesDownloaded,
|
|
1389
|
+
totalBytes,
|
|
1390
|
+
percentage: computeTransferPercentage(bytesDownloaded, totalBytes)
|
|
1391
|
+
};
|
|
1392
|
+
for (const listener of listeners) listener(info);
|
|
1393
|
+
});
|
|
1394
|
+
stream.pipe(writeStream);
|
|
1395
|
+
writeStream.on("finish", resolve);
|
|
1396
|
+
writeStream.on("error", reject);
|
|
1397
|
+
stream.on("error", reject);
|
|
1398
|
+
});
|
|
1399
|
+
} catch (err) {
|
|
1400
|
+
cleanupTempFile();
|
|
1401
|
+
if (controller.signal.aborted) {
|
|
1402
|
+
throw new DriveError("ABORTED", "Could not download file: the download was aborted");
|
|
1403
|
+
}
|
|
1404
|
+
throw new DriveError("DOWNLOAD_FAILED", `Could not download file: ${err instanceof Error ? err.message : "an unknown error occurred"}`);
|
|
1405
|
+
} finally {
|
|
1406
|
+
controller.signal.removeEventListener("abort", onAbort);
|
|
1407
|
+
}
|
|
1408
|
+
try {
|
|
1409
|
+
fs.renameSync(tempPath, cachePath);
|
|
1410
|
+
} catch (err) {
|
|
1411
|
+
if (err instanceof Error && "code" in err && err.code === "EXDEV") {
|
|
1412
|
+
fs.copyFileSync(tempPath, cachePath);
|
|
1413
|
+
fs.unlinkSync(tempPath);
|
|
1414
|
+
} else {
|
|
1415
|
+
cleanupTempFile();
|
|
1416
|
+
throw new DriveError("DOWNLOAD_FAILED", "Could not download file: failed to finalize the cached file");
|
|
1417
|
+
}
|
|
1418
|
+
}
|
|
1419
|
+
};
|
|
1420
|
+
var instrumentReadableForProgress = (source, totalBytes, options) => {
|
|
1421
|
+
if (!options?.onProgress && !options?.signal) return source;
|
|
1422
|
+
const passThrough = new PassThrough();
|
|
1423
|
+
let bytesDownloaded = 0;
|
|
1424
|
+
source.on("data", (chunk) => {
|
|
1425
|
+
bytesDownloaded += chunk.length;
|
|
1426
|
+
options.onProgress?.({
|
|
1427
|
+
bytesDownloaded,
|
|
1428
|
+
totalBytes,
|
|
1429
|
+
percentage: computeTransferPercentage(bytesDownloaded, totalBytes)
|
|
1430
|
+
});
|
|
1431
|
+
});
|
|
1432
|
+
source.on("error", (err) => passThrough.destroy(err));
|
|
1433
|
+
source.pipe(passThrough);
|
|
1434
|
+
if (options.signal) {
|
|
1435
|
+
const onAbort = () => {
|
|
1436
|
+
source.destroy(new DriveError("ABORTED", "Could not read file: the read was aborted"));
|
|
1437
|
+
};
|
|
1438
|
+
if (options.signal.aborted) onAbort();
|
|
1439
|
+
else options.signal.addEventListener("abort", onAbort, { once: true });
|
|
1440
|
+
}
|
|
1441
|
+
return passThrough;
|
|
1442
|
+
};
|
|
1443
|
+
var driveFilePath = async (file, options) => {
|
|
1319
1444
|
let drive;
|
|
1320
1445
|
if (typeof file === "string") {
|
|
1321
1446
|
const doc = await drive_default.findById(file);
|
|
1322
|
-
if (!doc) throw new
|
|
1447
|
+
if (!doc) throw new DriveError("NOT_FOUND", "Could not locate file: the file no longer exists");
|
|
1323
1448
|
drive = doc;
|
|
1324
1449
|
} else if ("toClient" in file) {
|
|
1325
1450
|
drive = file;
|
|
1326
1451
|
} else {
|
|
1327
|
-
throw new
|
|
1452
|
+
throw new DriveError("INVALID_REFERENCE", "Could not locate file: invalid file reference provided");
|
|
1328
1453
|
}
|
|
1329
1454
|
if (drive.information.type !== "FILE") {
|
|
1330
|
-
throw new
|
|
1455
|
+
throw new DriveError("INVALID_TYPE", "Could not locate file: this item is a folder, not a file");
|
|
1331
1456
|
}
|
|
1332
1457
|
const config = getDriveConfig();
|
|
1333
1458
|
const STORAGE_PATH = config.storage.path;
|
|
@@ -1335,7 +1460,7 @@ var driveFilePath = async (file) => {
|
|
|
1335
1460
|
if (providerType === "LOCAL") {
|
|
1336
1461
|
const filePath = path.join(STORAGE_PATH, "file", String(drive._id), "data.bin");
|
|
1337
1462
|
if (!fs.existsSync(filePath)) {
|
|
1338
|
-
throw new
|
|
1463
|
+
throw new DriveError("FILE_MISSING", "Could not locate file: the stored file is missing from disk");
|
|
1339
1464
|
}
|
|
1340
1465
|
return Object.freeze({
|
|
1341
1466
|
path: filePath,
|
|
@@ -1346,11 +1471,34 @@ var driveFilePath = async (file) => {
|
|
|
1346
1471
|
});
|
|
1347
1472
|
}
|
|
1348
1473
|
if (providerType === "GOOGLE") {
|
|
1349
|
-
const
|
|
1474
|
+
const driveId = String(drive._id);
|
|
1475
|
+
const fileDir = path.join(STORAGE_PATH, "file", driveId);
|
|
1350
1476
|
const cachedFilePath = path.join(fileDir, "data.bin");
|
|
1477
|
+
const expectedSize = drive.information.sizeInBytes;
|
|
1478
|
+
const isCached = () => {
|
|
1479
|
+
if (!fs.existsSync(cachedFilePath)) return false;
|
|
1480
|
+
return fs.statSync(cachedFilePath).size === expectedSize;
|
|
1481
|
+
};
|
|
1482
|
+
if (isCached()) {
|
|
1483
|
+
return Object.freeze({
|
|
1484
|
+
path: cachedFilePath,
|
|
1485
|
+
name: drive.name,
|
|
1486
|
+
mime: drive.information.mime,
|
|
1487
|
+
size: drive.information.sizeInBytes,
|
|
1488
|
+
provider: "GOOGLE"
|
|
1489
|
+
});
|
|
1490
|
+
}
|
|
1351
1491
|
if (fs.existsSync(cachedFilePath)) {
|
|
1352
|
-
|
|
1353
|
-
|
|
1492
|
+
fs.unlinkSync(cachedFilePath);
|
|
1493
|
+
}
|
|
1494
|
+
const accountId = drive.storageAccountId?.toString();
|
|
1495
|
+
const existingLock = cacheWriteLocks.get(driveId);
|
|
1496
|
+
if (existingLock) {
|
|
1497
|
+
try {
|
|
1498
|
+
await existingLock;
|
|
1499
|
+
} catch {
|
|
1500
|
+
}
|
|
1501
|
+
if (isCached()) {
|
|
1354
1502
|
return Object.freeze({
|
|
1355
1503
|
path: cachedFilePath,
|
|
1356
1504
|
name: drive.name,
|
|
@@ -1359,31 +1507,26 @@ var driveFilePath = async (file) => {
|
|
|
1359
1507
|
provider: "GOOGLE"
|
|
1360
1508
|
});
|
|
1361
1509
|
}
|
|
1362
|
-
fs.unlinkSync(cachedFilePath);
|
|
1363
1510
|
}
|
|
1364
|
-
|
|
1365
|
-
|
|
1366
|
-
|
|
1367
|
-
|
|
1511
|
+
let entry = inFlightGoogleDownloads.get(driveId);
|
|
1512
|
+
if (!entry) {
|
|
1513
|
+
const controller = new AbortController();
|
|
1514
|
+
const listeners = /* @__PURE__ */ new Set();
|
|
1515
|
+
const promise = downloadGoogleFileToCache(drive, accountId, cachedFilePath, expectedSize, listeners, controller).finally(() => {
|
|
1516
|
+
inFlightGoogleDownloads.delete(driveId);
|
|
1517
|
+
if (cacheWriteLocks.get(driveId) === promise) cacheWriteLocks.delete(driveId);
|
|
1518
|
+
});
|
|
1519
|
+
entry = { promise, controller, listeners };
|
|
1520
|
+
inFlightGoogleDownloads.set(driveId, entry);
|
|
1521
|
+
cacheWriteLocks.set(driveId, promise);
|
|
1368
1522
|
}
|
|
1369
|
-
const
|
|
1370
|
-
|
|
1371
|
-
|
|
1372
|
-
|
|
1373
|
-
|
|
1374
|
-
writeStream.on("error", reject);
|
|
1375
|
-
stream.on("error", reject);
|
|
1376
|
-
});
|
|
1377
|
-
try {
|
|
1378
|
-
fs.renameSync(tempPath, cachedFilePath);
|
|
1379
|
-
} catch (err) {
|
|
1380
|
-
if (err instanceof Error && "code" in err && err.code === "EXDEV") {
|
|
1381
|
-
fs.copyFileSync(tempPath, cachedFilePath);
|
|
1382
|
-
fs.unlinkSync(tempPath);
|
|
1383
|
-
} else {
|
|
1384
|
-
throw err;
|
|
1385
|
-
}
|
|
1523
|
+
const activeEntry = entry;
|
|
1524
|
+
if (options?.onProgress) activeEntry.listeners.add(options.onProgress);
|
|
1525
|
+
if (options?.signal) {
|
|
1526
|
+
if (options.signal.aborted) activeEntry.controller.abort();
|
|
1527
|
+
else options.signal.addEventListener("abort", () => activeEntry.controller.abort(), { once: true });
|
|
1386
1528
|
}
|
|
1529
|
+
await activeEntry.promise;
|
|
1387
1530
|
return Object.freeze({
|
|
1388
1531
|
path: cachedFilePath,
|
|
1389
1532
|
name: drive.name,
|
|
@@ -1392,7 +1535,7 @@ var driveFilePath = async (file) => {
|
|
|
1392
1535
|
provider: "GOOGLE"
|
|
1393
1536
|
});
|
|
1394
1537
|
}
|
|
1395
|
-
throw new
|
|
1538
|
+
throw new DriveError("UNSUPPORTED_PROVIDER", `Could not locate file: unsupported storage provider "${providerType}"`);
|
|
1396
1539
|
};
|
|
1397
1540
|
var driveList = async (options) => {
|
|
1398
1541
|
const { key, folderId, accountId, limit = 100, afterId } = options;
|
|
@@ -1400,7 +1543,7 @@ var driveList = async (options) => {
|
|
|
1400
1543
|
if (accountId && accountId !== "LOCAL") {
|
|
1401
1544
|
const account = await drive_default.db.model("StorageAccount").findOne({ _id: accountId, owner: key });
|
|
1402
1545
|
if (!account) {
|
|
1403
|
-
throw new
|
|
1546
|
+
throw new DriveError("ACCESS_DENIED", "Could not list files: storage account not found or access denied");
|
|
1404
1547
|
}
|
|
1405
1548
|
if (account.metadata.provider === "GOOGLE") {
|
|
1406
1549
|
providerName = "GOOGLE";
|
|
@@ -1428,7 +1571,7 @@ var driveListFiles = async (options) => {
|
|
|
1428
1571
|
if (accountId && accountId !== "LOCAL") {
|
|
1429
1572
|
const account = await drive_default.db.model("StorageAccount").findOne({ _id: accountId, owner: key });
|
|
1430
1573
|
if (!account) {
|
|
1431
|
-
throw new
|
|
1574
|
+
throw new DriveError("ACCESS_DENIED", "Could not load files: storage account not found or access denied");
|
|
1432
1575
|
}
|
|
1433
1576
|
if (account.metadata.provider === "GOOGLE") {
|
|
1434
1577
|
providerName = "GOOGLE";
|
|
@@ -1472,7 +1615,7 @@ var driveDelete = async (source, options) => {
|
|
|
1472
1615
|
let driveId;
|
|
1473
1616
|
if (typeof source === "string") {
|
|
1474
1617
|
const doc = await drive_default.findById(source);
|
|
1475
|
-
if (!doc) throw new
|
|
1618
|
+
if (!doc) throw new DriveError("NOT_FOUND", "Could not delete: the file no longer exists");
|
|
1476
1619
|
drive = doc;
|
|
1477
1620
|
driveId = source;
|
|
1478
1621
|
} else if ("toClient" in source) {
|
|
@@ -1480,7 +1623,7 @@ var driveDelete = async (source, options) => {
|
|
|
1480
1623
|
driveId = String(drive._id);
|
|
1481
1624
|
} else {
|
|
1482
1625
|
const doc = await drive_default.findById(source.id);
|
|
1483
|
-
if (!doc) throw new
|
|
1626
|
+
if (!doc) throw new DriveError("NOT_FOUND", "Could not delete: the selected file no longer exists");
|
|
1484
1627
|
drive = doc;
|
|
1485
1628
|
driveId = source.id;
|
|
1486
1629
|
}
|
|
@@ -1492,7 +1635,7 @@ var driveDelete = async (source, options) => {
|
|
|
1492
1635
|
trashedAt: null
|
|
1493
1636
|
});
|
|
1494
1637
|
if (childCount > 0) {
|
|
1495
|
-
throw new
|
|
1638
|
+
throw new DriveError("FOLDER_NOT_EMPTY", `Could not delete folder: it still contains ${childCount} item(s). Enable recursive delete to remove the folder and everything inside it.`);
|
|
1496
1639
|
}
|
|
1497
1640
|
}
|
|
1498
1641
|
const provider = drive.provider?.type === "GOOGLE" ? GoogleDriveProvider : LocalStorageProvider;
|
|
@@ -1503,17 +1646,17 @@ var driveDelete = async (source, options) => {
|
|
|
1503
1646
|
var resolveFolderByPath = async (folderPath, owner, accountId) => {
|
|
1504
1647
|
const normalizedPath = folderPath.replace(/^\/+|\/+$/g, "");
|
|
1505
1648
|
if (!normalizedPath) {
|
|
1506
|
-
throw new
|
|
1649
|
+
throw new DriveError("INVALID_PATH", "Could not resolve folder: the folder path is empty");
|
|
1507
1650
|
}
|
|
1508
1651
|
const segments = normalizedPath.split("/").filter((s) => s.length > 0);
|
|
1509
1652
|
if (segments.length === 0) {
|
|
1510
|
-
throw new
|
|
1653
|
+
throw new DriveError("INVALID_PATH", "Could not resolve folder: the folder path is invalid");
|
|
1511
1654
|
}
|
|
1512
1655
|
let providerName = "LOCAL";
|
|
1513
1656
|
if (accountId && accountId !== "LOCAL") {
|
|
1514
1657
|
const account = await drive_default.db.model("StorageAccount").findOne({ _id: accountId, owner });
|
|
1515
1658
|
if (!account) {
|
|
1516
|
-
throw new
|
|
1659
|
+
throw new DriveError("ACCESS_DENIED", "Could not resolve folder: storage account not found or access denied");
|
|
1517
1660
|
}
|
|
1518
1661
|
if (account.metadata.provider === "GOOGLE") {
|
|
1519
1662
|
providerName = "GOOGLE";
|
|
@@ -1547,7 +1690,7 @@ var driveUpload = async (source, key, options) => {
|
|
|
1547
1690
|
if (accountId && accountId !== "LOCAL") {
|
|
1548
1691
|
const account = await drive_default.db.model("StorageAccount").findOne({ _id: accountId, owner: key });
|
|
1549
1692
|
if (!account) {
|
|
1550
|
-
throw new
|
|
1693
|
+
throw new DriveError("ACCESS_DENIED", "Could not upload: storage account not found or access denied");
|
|
1551
1694
|
}
|
|
1552
1695
|
if (account.metadata.provider === "GOOGLE") {
|
|
1553
1696
|
provider = GoogleDriveProvider;
|
|
@@ -1558,7 +1701,7 @@ var driveUpload = async (source, key, options) => {
|
|
|
1558
1701
|
let fileSize;
|
|
1559
1702
|
if (typeof source === "string") {
|
|
1560
1703
|
if (!fs.existsSync(source)) {
|
|
1561
|
-
throw new
|
|
1704
|
+
throw new DriveError("FILE_MISSING", "Could not upload: source file not found");
|
|
1562
1705
|
}
|
|
1563
1706
|
sourceFilePath = source;
|
|
1564
1707
|
const stats = fs.statSync(source);
|
|
@@ -1617,17 +1760,17 @@ var driveUpload = async (source, key, options) => {
|
|
|
1617
1760
|
mimeType = mimeTypes[ext] || "application/octet-stream";
|
|
1618
1761
|
}
|
|
1619
1762
|
if (config.security && !validateMimeType(mimeType, config.security.allowedMimeTypes)) {
|
|
1620
|
-
throw new
|
|
1763
|
+
throw new DriveError("INVALID_MIME_TYPE", `Could not upload: file type "${mimeType}" is not allowed`);
|
|
1621
1764
|
}
|
|
1622
1765
|
if (config.security && fileSize > config.security.maxUploadSizeInBytes) {
|
|
1623
|
-
throw new
|
|
1766
|
+
throw new DriveError("FILE_TOO_LARGE", "Could not upload: file is larger than the maximum allowed size");
|
|
1624
1767
|
}
|
|
1625
1768
|
const isRootMode = config.mode === "ROOT";
|
|
1626
1769
|
if (!options.enforce && !isRootMode) {
|
|
1627
1770
|
const information = await getDriveInformation({ method: "KEY", key });
|
|
1628
1771
|
const quota = await provider.getQuota(key, accountId, information.storage.quotaInBytes);
|
|
1629
1772
|
if (quota.usedInBytes + fileSize > quota.quotaInBytes) {
|
|
1630
|
-
throw new
|
|
1773
|
+
throw new DriveError("QUOTA_EXCEEDED", "Could not upload: you have run out of storage space");
|
|
1631
1774
|
}
|
|
1632
1775
|
}
|
|
1633
1776
|
let resolvedParentId = null;
|
|
@@ -1739,7 +1882,7 @@ var driveCleanup = async () => {
|
|
|
1739
1882
|
}
|
|
1740
1883
|
}
|
|
1741
1884
|
try {
|
|
1742
|
-
const { getTusServer: getTusServer2 } = await import('./tus-
|
|
1885
|
+
const { getTusServer: getTusServer2 } = await import('./tus-ODCMFEBZ.js');
|
|
1743
1886
|
await getTusServer2().cleanUpExpiredUploads();
|
|
1744
1887
|
} catch (e) {
|
|
1745
1888
|
console.error("[next-drive] Failed to clean up expired tus uploads:", e);
|
|
@@ -1828,8 +1971,13 @@ var getTusServer = () => {
|
|
|
1828
1971
|
if (!ctx) return Number.MAX_SAFE_INTEGER;
|
|
1829
1972
|
return ctx.authenticated ? ctx.config.security?.maxUploadSizeInBytes ?? Number.MAX_SAFE_INTEGER : ctx.config.security?.unauthenticated?.maxUploadSizeInBytes ?? 0;
|
|
1830
1973
|
},
|
|
1831
|
-
// ** Keep the upload id in the query string so every method stays on `?action=upload`.
|
|
1832
|
-
|
|
1974
|
+
// ** Keep the upload id in the query string so every method stays on `?action=upload`. Derive
|
|
1975
|
+
// ** the path from the ACTUAL incoming request rather than the static `apiPath` this server was
|
|
1976
|
+
// ** constructed with - the two can drift apart (config.apiUrl is frozen on `globalThis` for the
|
|
1977
|
+
// ** life of the process, see getTusServer() above, so it can go stale after a proxy/route change
|
|
1978
|
+
// ** without a full restart). Mirroring the request's own path makes the continuation URL always
|
|
1979
|
+
// ** match wherever the client actually is, regardless of what `apiUrl` was configured with.
|
|
1980
|
+
generateUrl: (req, { id }) => `${new URL(req.url).pathname}?action=upload&id=${id}`,
|
|
1833
1981
|
// ** SECURITY: the id becomes a filesystem path inside FileStore (fs.unlink/read/resolve), and it
|
|
1834
1982
|
// ** arrives from the client-controlled query string. Only accept the exact format our
|
|
1835
1983
|
// ** namingFunction produces (32 lowercase hex) so a crafted `?id=../../..` cannot traverse out
|
|
@@ -1984,6 +2132,6 @@ var handleUpload = async (req, res, ctx) => {
|
|
|
1984
2132
|
res.end(response.body ? Buffer.from(await response.arrayBuffer()) : void 0);
|
|
1985
2133
|
};
|
|
1986
2134
|
|
|
1987
|
-
export { GoogleDriveProvider, LocalStorageProvider, account_default, driveCleanup, driveConfiguration, driveConfirm, driveDelete, driveFilePath, driveGetUrl, driveInfo, driveList, driveListFiles, drivePurgeExpired, driveReadFile, driveUpload, drive_default, getDriveConfig, getDriveInformation, getImageSettings, getTusServer, handleUpload, resolveProvider, resolveTusCorsOptions, withSignedUrl, withSignedUrls };
|
|
1988
|
-
//# sourceMappingURL=chunk-
|
|
1989
|
-
//# sourceMappingURL=chunk-
|
|
2135
|
+
export { DriveError, GoogleDriveProvider, LocalStorageProvider, account_default, driveCleanup, driveConfiguration, driveConfirm, driveDelete, driveFilePath, driveGetUrl, driveInfo, driveList, driveListFiles, drivePurgeExpired, driveReadFile, driveUpload, drive_default, getDriveConfig, getDriveInformation, getImageSettings, getTusServer, handleUpload, resolveProvider, resolveTusCorsOptions, withSignedUrl, withSignedUrls };
|
|
2136
|
+
//# sourceMappingURL=chunk-QMPV4YSZ.js.map
|
|
2137
|
+
//# sourceMappingURL=chunk-QMPV4YSZ.js.map
|