@muhgholy/next-drive 4.3.0 → 4.5.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-25MNL2OG.cjs → chunk-KQGZXSKY.cjs} +98 -11
- package/dist/chunk-KQGZXSKY.cjs.map +1 -0
- package/dist/{chunk-MTGJTRD5.js → chunk-YUU5BFE7.js} +98 -11
- package/dist/chunk-YUU5BFE7.js.map +1 -0
- package/dist/client/index.cjs +1 -1
- package/dist/client/index.cjs.map +1 -1
- package/dist/client/index.js +1 -1
- package/dist/client/index.js.map +1 -1
- 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/dist/server/utils/migration.d.ts.map +1 -1
- package/dist/server/utils.d.ts +1 -0
- package/dist/server/utils.d.ts.map +1 -1
- package/package.json +1 -1
- package/dist/chunk-25MNL2OG.cjs.map +0 -1
- package/dist/chunk-MTGJTRD5.js.map +0 -1
|
@@ -68,6 +68,24 @@ var drive_default = Drive;
|
|
|
68
68
|
// src/server/utils/migration.ts
|
|
69
69
|
var MIGRATION_FILE = ".migration-version";
|
|
70
70
|
var CURRENT_VERSION = 1;
|
|
71
|
+
var isReadyForMigration = () => {
|
|
72
|
+
if (mongoose.connection.readyState !== 1) {
|
|
73
|
+
console.warn("[next-drive] Migration skipped: Database not connected");
|
|
74
|
+
return false;
|
|
75
|
+
}
|
|
76
|
+
return true;
|
|
77
|
+
};
|
|
78
|
+
var isNewInstallation = (storagePath) => {
|
|
79
|
+
if (!fs.existsSync(storagePath)) return true;
|
|
80
|
+
const hasOldDriveDir = fs.existsSync(path.join(storagePath, "drive"));
|
|
81
|
+
const hasOldCacheDir = fs.existsSync(path.join(storagePath, "cache", "thumbnails"));
|
|
82
|
+
const hasOldLibraryDir = fs.existsSync(path.join(storagePath, "library", "google"));
|
|
83
|
+
const hasRootLevelFiles = fs.existsSync(storagePath) && fs.readdirSync(storagePath).some((entry) => {
|
|
84
|
+
const entryPath = path.join(storagePath, entry);
|
|
85
|
+
return fs.statSync(entryPath).isDirectory() && /^[a-f0-9]{24}$/i.test(entry) && entry !== "file";
|
|
86
|
+
});
|
|
87
|
+
return !hasOldDriveDir && !hasOldCacheDir && !hasOldLibraryDir && !hasRootLevelFiles;
|
|
88
|
+
};
|
|
71
89
|
var migrations = [
|
|
72
90
|
{
|
|
73
91
|
version: 1,
|
|
@@ -185,6 +203,12 @@ var migrations = [
|
|
|
185
203
|
}
|
|
186
204
|
];
|
|
187
205
|
var runMigrations = async (storagePath) => {
|
|
206
|
+
if (!isReadyForMigration()) {
|
|
207
|
+
return;
|
|
208
|
+
}
|
|
209
|
+
if (!fs.existsSync(storagePath)) {
|
|
210
|
+
fs.mkdirSync(storagePath, { recursive: true });
|
|
211
|
+
}
|
|
188
212
|
const versionFile = path.join(storagePath, MIGRATION_FILE);
|
|
189
213
|
let currentVersion = 0;
|
|
190
214
|
if (fs.existsSync(versionFile)) {
|
|
@@ -195,8 +219,10 @@ var runMigrations = async (storagePath) => {
|
|
|
195
219
|
}
|
|
196
220
|
}
|
|
197
221
|
if (currentVersion >= CURRENT_VERSION) return;
|
|
198
|
-
if (
|
|
199
|
-
|
|
222
|
+
if (isNewInstallation(storagePath)) {
|
|
223
|
+
console.log("[next-drive] New installation detected, skipping migration");
|
|
224
|
+
fs.writeFileSync(versionFile, String(CURRENT_VERSION));
|
|
225
|
+
return;
|
|
200
226
|
}
|
|
201
227
|
const pendingMigrations = migrations.filter((m) => m.version > currentVersion);
|
|
202
228
|
for (const migration of pendingMigrations.sort((a, b) => a.version - b.version)) {
|
|
@@ -213,14 +239,15 @@ var runMigrations = async (storagePath) => {
|
|
|
213
239
|
|
|
214
240
|
// src/server/config.ts
|
|
215
241
|
var globalConfig = null;
|
|
216
|
-
var
|
|
242
|
+
var migrationPromise = null;
|
|
243
|
+
var configInitialized = false;
|
|
217
244
|
var driveConfiguration = async (config) => {
|
|
218
245
|
if (mongoose.connection.readyState !== 1) {
|
|
219
246
|
throw new Error("Database not connected. Please connect to Mongoose before initializing next-drive.");
|
|
220
247
|
}
|
|
221
|
-
if (
|
|
222
|
-
|
|
223
|
-
|
|
248
|
+
if (configInitialized && globalConfig) {
|
|
249
|
+
if (migrationPromise) await migrationPromise;
|
|
250
|
+
return globalConfig;
|
|
224
251
|
}
|
|
225
252
|
const mode = config.mode || "NORMAL";
|
|
226
253
|
if (mode === "ROOT") {
|
|
@@ -233,7 +260,6 @@ var driveConfiguration = async (config) => {
|
|
|
233
260
|
allowedMimeTypes: ["*/*"]
|
|
234
261
|
}
|
|
235
262
|
};
|
|
236
|
-
return globalConfig;
|
|
237
263
|
} else {
|
|
238
264
|
if (!config.information) {
|
|
239
265
|
throw new Error("information callback is required in NORMAL mode");
|
|
@@ -249,8 +275,13 @@ var driveConfiguration = async (config) => {
|
|
|
249
275
|
},
|
|
250
276
|
information: config.information
|
|
251
277
|
};
|
|
252
|
-
return globalConfig;
|
|
253
278
|
}
|
|
279
|
+
configInitialized = true;
|
|
280
|
+
if (!migrationPromise) {
|
|
281
|
+
migrationPromise = runMigrations(config.storage.path);
|
|
282
|
+
}
|
|
283
|
+
await migrationPromise;
|
|
284
|
+
return globalConfig;
|
|
254
285
|
};
|
|
255
286
|
var getDriveConfig = () => {
|
|
256
287
|
if (!globalConfig) throw new Error("Drive configuration not initialized");
|
|
@@ -325,6 +356,14 @@ var extractImageMetadata = async (filePath) => {
|
|
|
325
356
|
return null;
|
|
326
357
|
}
|
|
327
358
|
};
|
|
359
|
+
var parseQuality = (q) => {
|
|
360
|
+
if (!q) return 80;
|
|
361
|
+
if (q === "low") return 40;
|
|
362
|
+
if (q === "medium") return 60;
|
|
363
|
+
if (q === "high") return 80;
|
|
364
|
+
const n = parseInt(q, 10);
|
|
365
|
+
return isNaN(n) ? 80 : Math.min(100, Math.max(1, n));
|
|
366
|
+
};
|
|
328
367
|
var objectIdSchema = z.string().refine((val) => isValidObjectId(val), {
|
|
329
368
|
message: "Invalid ObjectId format"
|
|
330
369
|
});
|
|
@@ -1511,11 +1550,59 @@ var driveAPIHandler = async (req, res) => {
|
|
|
1511
1550
|
if (action === "serve") {
|
|
1512
1551
|
const { stream, mime, size } = await itemProvider.openStream(drive, itemAccountId);
|
|
1513
1552
|
const safeFilename = sanitizeContentDispositionFilename(drive.name);
|
|
1553
|
+
const format = req.query.format;
|
|
1554
|
+
const quality = req.query.quality;
|
|
1555
|
+
const isImage = mime.startsWith("image/");
|
|
1556
|
+
const shouldTransform = isImage && (format || quality);
|
|
1514
1557
|
res.setHeader("Content-Disposition", `inline; filename="${safeFilename}"`);
|
|
1515
|
-
res.setHeader("Content-Type", mime);
|
|
1516
1558
|
if (config.cors?.enabled) {
|
|
1517
1559
|
res.setHeader("Cross-Origin-Resource-Policy", "cross-origin");
|
|
1518
1560
|
}
|
|
1561
|
+
if (shouldTransform) {
|
|
1562
|
+
try {
|
|
1563
|
+
const qValue = parseQuality(quality);
|
|
1564
|
+
let targetFormat = format || mime.split("/")[1];
|
|
1565
|
+
if (targetFormat === "jpg") targetFormat = "jpeg";
|
|
1566
|
+
const cacheDir = path.join(config.storage.path, "file", drive._id.toString(), "cache");
|
|
1567
|
+
const cacheFilename = `optimized_q${qValue}_${targetFormat}.bin`;
|
|
1568
|
+
const cachePath = path.join(cacheDir, cacheFilename);
|
|
1569
|
+
if (fs.existsSync(cachePath)) {
|
|
1570
|
+
const cacheStat = fs.statSync(cachePath);
|
|
1571
|
+
res.setHeader("Content-Type", `image/${targetFormat}`);
|
|
1572
|
+
res.setHeader("Content-Length", cacheStat.size);
|
|
1573
|
+
res.setHeader("Cache-Control", "public, max-age=31536000, immutable");
|
|
1574
|
+
if (config.cors?.enabled) {
|
|
1575
|
+
res.setHeader("Cross-Origin-Resource-Policy", "cross-origin");
|
|
1576
|
+
}
|
|
1577
|
+
if ("destroy" in stream) stream.destroy();
|
|
1578
|
+
fs.createReadStream(cachePath).pipe(res);
|
|
1579
|
+
return;
|
|
1580
|
+
}
|
|
1581
|
+
if (!fs.existsSync(cacheDir)) fs.mkdirSync(cacheDir, { recursive: true });
|
|
1582
|
+
const pipeline = sharp();
|
|
1583
|
+
if (targetFormat === "jpeg") {
|
|
1584
|
+
pipeline.jpeg({ quality: qValue, mozjpeg: true });
|
|
1585
|
+
res.setHeader("Content-Type", "image/jpeg");
|
|
1586
|
+
} else if (targetFormat === "png") {
|
|
1587
|
+
pipeline.png({ quality: qValue });
|
|
1588
|
+
res.setHeader("Content-Type", "image/png");
|
|
1589
|
+
} else if (targetFormat === "webp") {
|
|
1590
|
+
pipeline.webp({ quality: qValue });
|
|
1591
|
+
res.setHeader("Content-Type", "image/webp");
|
|
1592
|
+
} else if (targetFormat === "avif") {
|
|
1593
|
+
pipeline.avif({ quality: qValue });
|
|
1594
|
+
res.setHeader("Content-Type", "image/avif");
|
|
1595
|
+
}
|
|
1596
|
+
res.setHeader("Cache-Control", "public, max-age=31536000, immutable");
|
|
1597
|
+
stream.pipe(pipeline);
|
|
1598
|
+
pipeline.clone().toFile(cachePath).catch((e) => console.error("[next-drive] Cache write failed:", e));
|
|
1599
|
+
pipeline.clone().pipe(res);
|
|
1600
|
+
return;
|
|
1601
|
+
} catch (e) {
|
|
1602
|
+
console.error("[next-drive] Image transformation failed:", e);
|
|
1603
|
+
}
|
|
1604
|
+
}
|
|
1605
|
+
res.setHeader("Content-Type", mime);
|
|
1519
1606
|
if (size) res.setHeader("Content-Length", size);
|
|
1520
1607
|
stream.pipe(res);
|
|
1521
1608
|
return;
|
|
@@ -2050,5 +2137,5 @@ var driveAPIHandler = async (req, res) => {
|
|
|
2050
2137
|
};
|
|
2051
2138
|
|
|
2052
2139
|
export { driveAPIHandler, driveConfiguration, driveDelete, driveFilePath, driveFileSchemaZod, driveGetUrl, driveInfo, driveList, driveReadFile, driveUpload, getDriveConfig, getDriveInformation };
|
|
2053
|
-
//# sourceMappingURL=chunk-
|
|
2054
|
-
//# sourceMappingURL=chunk-
|
|
2140
|
+
//# sourceMappingURL=chunk-YUU5BFE7.js.map
|
|
2141
|
+
//# sourceMappingURL=chunk-YUU5BFE7.js.map
|