@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 +6 -6
- package/dist/{chunk-7PTOIRBL.cjs → chunk-LMM5IU5U.cjs} +70 -47
- package/dist/chunk-LMM5IU5U.cjs.map +1 -0
- package/dist/{chunk-NKP44P4G.js → chunk-RQ3YRDNR.js} +69 -46
- package/dist/chunk-RQ3YRDNR.js.map +1 -0
- 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-7PTOIRBL.cjs.map +0 -1
- package/dist/chunk-NKP44P4G.js.map +0 -1
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 =
|
|
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
|
-
|
|
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
|
-
|
|
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 =
|
|
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-${
|
|
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-${
|
|
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 =
|
|
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-
|
|
1804
|
-
//# sourceMappingURL=chunk-
|
|
1826
|
+
//# sourceMappingURL=chunk-LMM5IU5U.cjs.map
|
|
1827
|
+
//# sourceMappingURL=chunk-LMM5IU5U.cjs.map
|