@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
|
@@ -81,6 +81,24 @@ var drive_default = Drive;
|
|
|
81
81
|
// src/server/utils/migration.ts
|
|
82
82
|
var MIGRATION_FILE = ".migration-version";
|
|
83
83
|
var CURRENT_VERSION = 1;
|
|
84
|
+
var isReadyForMigration = () => {
|
|
85
|
+
if (mongoose__default.default.connection.readyState !== 1) {
|
|
86
|
+
console.warn("[next-drive] Migration skipped: Database not connected");
|
|
87
|
+
return false;
|
|
88
|
+
}
|
|
89
|
+
return true;
|
|
90
|
+
};
|
|
91
|
+
var isNewInstallation = (storagePath) => {
|
|
92
|
+
if (!fs__default.default.existsSync(storagePath)) return true;
|
|
93
|
+
const hasOldDriveDir = fs__default.default.existsSync(path__default.default.join(storagePath, "drive"));
|
|
94
|
+
const hasOldCacheDir = fs__default.default.existsSync(path__default.default.join(storagePath, "cache", "thumbnails"));
|
|
95
|
+
const hasOldLibraryDir = fs__default.default.existsSync(path__default.default.join(storagePath, "library", "google"));
|
|
96
|
+
const hasRootLevelFiles = fs__default.default.existsSync(storagePath) && fs__default.default.readdirSync(storagePath).some((entry) => {
|
|
97
|
+
const entryPath = path__default.default.join(storagePath, entry);
|
|
98
|
+
return fs__default.default.statSync(entryPath).isDirectory() && /^[a-f0-9]{24}$/i.test(entry) && entry !== "file";
|
|
99
|
+
});
|
|
100
|
+
return !hasOldDriveDir && !hasOldCacheDir && !hasOldLibraryDir && !hasRootLevelFiles;
|
|
101
|
+
};
|
|
84
102
|
var migrations = [
|
|
85
103
|
{
|
|
86
104
|
version: 1,
|
|
@@ -198,6 +216,12 @@ var migrations = [
|
|
|
198
216
|
}
|
|
199
217
|
];
|
|
200
218
|
var runMigrations = async (storagePath) => {
|
|
219
|
+
if (!isReadyForMigration()) {
|
|
220
|
+
return;
|
|
221
|
+
}
|
|
222
|
+
if (!fs__default.default.existsSync(storagePath)) {
|
|
223
|
+
fs__default.default.mkdirSync(storagePath, { recursive: true });
|
|
224
|
+
}
|
|
201
225
|
const versionFile = path__default.default.join(storagePath, MIGRATION_FILE);
|
|
202
226
|
let currentVersion = 0;
|
|
203
227
|
if (fs__default.default.existsSync(versionFile)) {
|
|
@@ -208,8 +232,10 @@ var runMigrations = async (storagePath) => {
|
|
|
208
232
|
}
|
|
209
233
|
}
|
|
210
234
|
if (currentVersion >= CURRENT_VERSION) return;
|
|
211
|
-
if (
|
|
212
|
-
|
|
235
|
+
if (isNewInstallation(storagePath)) {
|
|
236
|
+
console.log("[next-drive] New installation detected, skipping migration");
|
|
237
|
+
fs__default.default.writeFileSync(versionFile, String(CURRENT_VERSION));
|
|
238
|
+
return;
|
|
213
239
|
}
|
|
214
240
|
const pendingMigrations = migrations.filter((m) => m.version > currentVersion);
|
|
215
241
|
for (const migration of pendingMigrations.sort((a, b) => a.version - b.version)) {
|
|
@@ -226,14 +252,15 @@ var runMigrations = async (storagePath) => {
|
|
|
226
252
|
|
|
227
253
|
// src/server/config.ts
|
|
228
254
|
var globalConfig = null;
|
|
229
|
-
var
|
|
255
|
+
var migrationPromise = null;
|
|
256
|
+
var configInitialized = false;
|
|
230
257
|
var driveConfiguration = async (config) => {
|
|
231
258
|
if (mongoose__default.default.connection.readyState !== 1) {
|
|
232
259
|
throw new Error("Database not connected. Please connect to Mongoose before initializing next-drive.");
|
|
233
260
|
}
|
|
234
|
-
if (
|
|
235
|
-
|
|
236
|
-
|
|
261
|
+
if (configInitialized && globalConfig) {
|
|
262
|
+
if (migrationPromise) await migrationPromise;
|
|
263
|
+
return globalConfig;
|
|
237
264
|
}
|
|
238
265
|
const mode = config.mode || "NORMAL";
|
|
239
266
|
if (mode === "ROOT") {
|
|
@@ -246,7 +273,6 @@ var driveConfiguration = async (config) => {
|
|
|
246
273
|
allowedMimeTypes: ["*/*"]
|
|
247
274
|
}
|
|
248
275
|
};
|
|
249
|
-
return globalConfig;
|
|
250
276
|
} else {
|
|
251
277
|
if (!config.information) {
|
|
252
278
|
throw new Error("information callback is required in NORMAL mode");
|
|
@@ -262,8 +288,13 @@ var driveConfiguration = async (config) => {
|
|
|
262
288
|
},
|
|
263
289
|
information: config.information
|
|
264
290
|
};
|
|
265
|
-
return globalConfig;
|
|
266
291
|
}
|
|
292
|
+
configInitialized = true;
|
|
293
|
+
if (!migrationPromise) {
|
|
294
|
+
migrationPromise = runMigrations(config.storage.path);
|
|
295
|
+
}
|
|
296
|
+
await migrationPromise;
|
|
297
|
+
return globalConfig;
|
|
267
298
|
};
|
|
268
299
|
var getDriveConfig = () => {
|
|
269
300
|
if (!globalConfig) throw new Error("Drive configuration not initialized");
|
|
@@ -338,6 +369,14 @@ var extractImageMetadata = async (filePath) => {
|
|
|
338
369
|
return null;
|
|
339
370
|
}
|
|
340
371
|
};
|
|
372
|
+
var parseQuality = (q) => {
|
|
373
|
+
if (!q) return 80;
|
|
374
|
+
if (q === "low") return 40;
|
|
375
|
+
if (q === "medium") return 60;
|
|
376
|
+
if (q === "high") return 80;
|
|
377
|
+
const n = parseInt(q, 10);
|
|
378
|
+
return isNaN(n) ? 80 : Math.min(100, Math.max(1, n));
|
|
379
|
+
};
|
|
341
380
|
var objectIdSchema = zod.z.string().refine((val) => mongoose.isValidObjectId(val), {
|
|
342
381
|
message: "Invalid ObjectId format"
|
|
343
382
|
});
|
|
@@ -1524,11 +1563,59 @@ var driveAPIHandler = async (req, res) => {
|
|
|
1524
1563
|
if (action === "serve") {
|
|
1525
1564
|
const { stream, mime, size } = await itemProvider.openStream(drive, itemAccountId);
|
|
1526
1565
|
const safeFilename = sanitizeContentDispositionFilename(drive.name);
|
|
1566
|
+
const format = req.query.format;
|
|
1567
|
+
const quality = req.query.quality;
|
|
1568
|
+
const isImage = mime.startsWith("image/");
|
|
1569
|
+
const shouldTransform = isImage && (format || quality);
|
|
1527
1570
|
res.setHeader("Content-Disposition", `inline; filename="${safeFilename}"`);
|
|
1528
|
-
res.setHeader("Content-Type", mime);
|
|
1529
1571
|
if (config.cors?.enabled) {
|
|
1530
1572
|
res.setHeader("Cross-Origin-Resource-Policy", "cross-origin");
|
|
1531
1573
|
}
|
|
1574
|
+
if (shouldTransform) {
|
|
1575
|
+
try {
|
|
1576
|
+
const qValue = parseQuality(quality);
|
|
1577
|
+
let targetFormat = format || mime.split("/")[1];
|
|
1578
|
+
if (targetFormat === "jpg") targetFormat = "jpeg";
|
|
1579
|
+
const cacheDir = path__default.default.join(config.storage.path, "file", drive._id.toString(), "cache");
|
|
1580
|
+
const cacheFilename = `optimized_q${qValue}_${targetFormat}.bin`;
|
|
1581
|
+
const cachePath = path__default.default.join(cacheDir, cacheFilename);
|
|
1582
|
+
if (fs__default.default.existsSync(cachePath)) {
|
|
1583
|
+
const cacheStat = fs__default.default.statSync(cachePath);
|
|
1584
|
+
res.setHeader("Content-Type", `image/${targetFormat}`);
|
|
1585
|
+
res.setHeader("Content-Length", cacheStat.size);
|
|
1586
|
+
res.setHeader("Cache-Control", "public, max-age=31536000, immutable");
|
|
1587
|
+
if (config.cors?.enabled) {
|
|
1588
|
+
res.setHeader("Cross-Origin-Resource-Policy", "cross-origin");
|
|
1589
|
+
}
|
|
1590
|
+
if ("destroy" in stream) stream.destroy();
|
|
1591
|
+
fs__default.default.createReadStream(cachePath).pipe(res);
|
|
1592
|
+
return;
|
|
1593
|
+
}
|
|
1594
|
+
if (!fs__default.default.existsSync(cacheDir)) fs__default.default.mkdirSync(cacheDir, { recursive: true });
|
|
1595
|
+
const pipeline = sharp__default.default();
|
|
1596
|
+
if (targetFormat === "jpeg") {
|
|
1597
|
+
pipeline.jpeg({ quality: qValue, mozjpeg: true });
|
|
1598
|
+
res.setHeader("Content-Type", "image/jpeg");
|
|
1599
|
+
} else if (targetFormat === "png") {
|
|
1600
|
+
pipeline.png({ quality: qValue });
|
|
1601
|
+
res.setHeader("Content-Type", "image/png");
|
|
1602
|
+
} else if (targetFormat === "webp") {
|
|
1603
|
+
pipeline.webp({ quality: qValue });
|
|
1604
|
+
res.setHeader("Content-Type", "image/webp");
|
|
1605
|
+
} else if (targetFormat === "avif") {
|
|
1606
|
+
pipeline.avif({ quality: qValue });
|
|
1607
|
+
res.setHeader("Content-Type", "image/avif");
|
|
1608
|
+
}
|
|
1609
|
+
res.setHeader("Cache-Control", "public, max-age=31536000, immutable");
|
|
1610
|
+
stream.pipe(pipeline);
|
|
1611
|
+
pipeline.clone().toFile(cachePath).catch((e) => console.error("[next-drive] Cache write failed:", e));
|
|
1612
|
+
pipeline.clone().pipe(res);
|
|
1613
|
+
return;
|
|
1614
|
+
} catch (e) {
|
|
1615
|
+
console.error("[next-drive] Image transformation failed:", e);
|
|
1616
|
+
}
|
|
1617
|
+
}
|
|
1618
|
+
res.setHeader("Content-Type", mime);
|
|
1532
1619
|
if (size) res.setHeader("Content-Length", size);
|
|
1533
1620
|
stream.pipe(res);
|
|
1534
1621
|
return;
|
|
@@ -2074,5 +2161,5 @@ exports.driveReadFile = driveReadFile;
|
|
|
2074
2161
|
exports.driveUpload = driveUpload;
|
|
2075
2162
|
exports.getDriveConfig = getDriveConfig;
|
|
2076
2163
|
exports.getDriveInformation = getDriveInformation;
|
|
2077
|
-
//# sourceMappingURL=chunk-
|
|
2078
|
-
//# sourceMappingURL=chunk-
|
|
2164
|
+
//# sourceMappingURL=chunk-KQGZXSKY.cjs.map
|
|
2165
|
+
//# sourceMappingURL=chunk-KQGZXSKY.cjs.map
|