@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.
- package/dist/{chunk-SSOIUMFQ.js → chunk-N3C7IKRK.js} +61 -58
- package/dist/chunk-N3C7IKRK.js.map +1 -0
- package/dist/{chunk-JZJKT2OS.cjs → chunk-XIUFYDVE.cjs} +62 -59
- package/dist/chunk-XIUFYDVE.cjs.map +1 -0
- package/dist/server/config.d.ts.map +1 -1
- package/dist/server/express.cjs +11 -11
- package/dist/server/express.js +2 -2
- package/dist/server/index.cjs +13 -13
- package/dist/server/index.d.ts.map +1 -1
- package/dist/server/index.js +1 -1
- package/package.json +1 -1
- package/dist/chunk-JZJKT2OS.cjs.map +0 -1
- package/dist/chunk-SSOIUMFQ.js.map +0 -1
|
@@ -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
|
|
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
|
|
242
|
-
var
|
|
243
|
-
|
|
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 (
|
|
249
|
-
if (migrationPromise) await migrationPromise;
|
|
250
|
-
return
|
|
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
|
-
|
|
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:
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
303
|
+
await g.migrationPromise;
|
|
304
|
+
return g.config;
|
|
294
305
|
};
|
|
295
306
|
var getDriveConfig = () => {
|
|
296
|
-
|
|
297
|
-
|
|
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 =
|
|
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 =
|
|
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-${
|
|
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-${
|
|
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 =
|
|
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
|
-
|
|
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
|
-
|
|
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 =
|
|
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
|
-
|
|
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-
|
|
2402
|
-
//# sourceMappingURL=chunk-
|
|
2404
|
+
//# sourceMappingURL=chunk-N3C7IKRK.js.map
|
|
2405
|
+
//# sourceMappingURL=chunk-N3C7IKRK.js.map
|