@muhgholy/next-drive 4.1.0 → 4.3.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/README.md +18 -33
- package/dist/{chunk-OGVU5UBC.cjs → chunk-25MNL2OG.cjs} +369 -190
- package/dist/chunk-25MNL2OG.cjs.map +1 -0
- package/dist/{chunk-RGE7EYMF.js → chunk-MTGJTRD5.js} +360 -181
- package/dist/chunk-MTGJTRD5.js.map +1 -0
- package/dist/{chunk-CFLH4TCQ.cjs → chunk-TA6L5FYG.cjs} +3 -22
- package/dist/chunk-TA6L5FYG.cjs.map +1 -0
- package/dist/{chunk-T43C7MQ6.js → chunk-TMSG5WJZ.js} +4 -22
- package/dist/chunk-TMSG5WJZ.js.map +1 -0
- package/dist/client/components/dialog.d.ts.map +1 -1
- package/dist/client/components/drive/explorer.d.ts.map +1 -1
- package/dist/client/components/drive/file-grid.d.ts.map +1 -1
- package/dist/client/context.d.ts +2 -9
- package/dist/client/context.d.ts.map +1 -1
- package/dist/client/hooks/useUpload.d.ts +1 -1
- package/dist/client/hooks/useUpload.d.ts.map +1 -1
- package/dist/client/index.cjs +118 -118
- package/dist/client/index.cjs.map +1 -1
- package/dist/client/index.css +1 -1
- package/dist/client/index.d.ts +1 -2
- package/dist/client/index.d.ts.map +1 -1
- package/dist/client/index.js +15 -15
- package/dist/client/index.js.map +1 -1
- package/dist/client/utils.d.ts +2 -9
- package/dist/client/utils.d.ts.map +1 -1
- package/dist/server/config.d.ts +1 -1
- package/dist/server/config.d.ts.map +1 -1
- package/dist/server/controllers/drive.d.ts +0 -2
- package/dist/server/controllers/drive.d.ts.map +1 -1
- package/dist/server/express.cjs +12 -12
- package/dist/server/express.cjs.map +1 -1
- package/dist/server/express.d.ts +2 -2
- package/dist/server/express.d.ts.map +1 -1
- package/dist/server/express.js +3 -3
- package/dist/server/express.js.map +1 -1
- package/dist/server/index.cjs +15 -19
- package/dist/server/index.d.ts +2 -2
- package/dist/server/index.d.ts.map +1 -1
- package/dist/server/index.js +2 -2
- package/dist/server/providers/google.d.ts.map +1 -1
- package/dist/server/providers/local.d.ts.map +1 -1
- package/dist/server/utils/ffmpeg.d.ts +7 -13
- package/dist/server/utils/ffmpeg.d.ts.map +1 -1
- package/dist/server/utils/folderValidation.d.ts +12 -6
- package/dist/server/utils/folderValidation.d.ts.map +1 -1
- package/dist/server/utils/imageConvert.d.ts +7 -14
- package/dist/server/utils/imageConvert.d.ts.map +1 -1
- package/dist/server/utils/metadata.d.ts +4 -22
- package/dist/server/utils/metadata.d.ts.map +1 -1
- package/dist/server/utils/migration.d.ts +2 -0
- package/dist/server/utils/migration.d.ts.map +1 -0
- package/dist/server/utils.d.ts +0 -1
- package/dist/server/utils.d.ts.map +1 -1
- package/dist/server/zod/schemas.d.ts +2 -8
- package/dist/server/zod/schemas.d.ts.map +1 -1
- package/dist/types/client/index.d.ts +0 -2
- package/dist/types/client/index.d.ts.map +1 -1
- package/dist/types/lib/database/storage/account.d.ts +1 -1
- package/dist/types/lib/database/storage/account.d.ts.map +1 -1
- package/dist/types/server/config.d.ts +0 -5
- package/dist/types/server/config.d.ts.map +1 -1
- package/dist/types/server/express.d.ts +1 -5
- package/dist/types/server/express.d.ts.map +1 -1
- package/dist/types/server/index.d.ts +2 -0
- package/dist/types/server/index.d.ts.map +1 -1
- package/dist/types/server/metadata.d.ts +11 -0
- package/dist/types/server/metadata.d.ts.map +1 -0
- package/dist/types/server/migration.d.ts +6 -0
- package/dist/types/server/migration.d.ts.map +1 -0
- package/package.json +1 -1
- package/dist/chunk-CFLH4TCQ.cjs.map +0 -1
- package/dist/chunk-OGVU5UBC.cjs.map +0 -1
- package/dist/chunk-RGE7EYMF.js.map +0 -1
- package/dist/chunk-T43C7MQ6.js.map +0 -1
- package/dist/client/index.css.map +0 -1
|
@@ -1,68 +1,15 @@
|
|
|
1
1
|
import formidable from 'formidable';
|
|
2
|
-
import
|
|
3
|
-
import
|
|
2
|
+
import path from 'path';
|
|
3
|
+
import fs from 'fs';
|
|
4
4
|
import os2 from 'os';
|
|
5
5
|
import crypto2 from 'crypto';
|
|
6
|
-
import
|
|
6
|
+
import mongoose, { Schema, isValidObjectId } from 'mongoose';
|
|
7
7
|
import sharp from 'sharp';
|
|
8
8
|
import { z } from 'zod';
|
|
9
9
|
import ffmpeg from 'fluent-ffmpeg';
|
|
10
10
|
import { google } from 'googleapis';
|
|
11
11
|
|
|
12
12
|
// src/server/index.ts
|
|
13
|
-
var globalConfig = null;
|
|
14
|
-
var driveConfiguration = (config) => {
|
|
15
|
-
if (mongoose2.connection.readyState !== 1) {
|
|
16
|
-
throw new Error("Database not connected. Please connect to Mongoose before initializing next-drive.");
|
|
17
|
-
}
|
|
18
|
-
const mode = config.mode || "NORMAL";
|
|
19
|
-
if (mode === "ROOT") {
|
|
20
|
-
globalConfig = {
|
|
21
|
-
...config,
|
|
22
|
-
mode: "ROOT",
|
|
23
|
-
security: config.security || {
|
|
24
|
-
maxUploadSizeInBytes: 1024 * 1024 * 1024 * 10,
|
|
25
|
-
// 10GB default for ROOT
|
|
26
|
-
allowedMimeTypes: ["*/*"]
|
|
27
|
-
}
|
|
28
|
-
};
|
|
29
|
-
return globalConfig;
|
|
30
|
-
} else {
|
|
31
|
-
if (!config.information) {
|
|
32
|
-
throw new Error("information callback is required in NORMAL mode");
|
|
33
|
-
}
|
|
34
|
-
globalConfig = {
|
|
35
|
-
...config,
|
|
36
|
-
mode: "NORMAL",
|
|
37
|
-
security: {
|
|
38
|
-
maxUploadSizeInBytes: config.security?.maxUploadSizeInBytes ?? 10 * 1024 * 1024,
|
|
39
|
-
allowedMimeTypes: config.security?.allowedMimeTypes ?? ["*/*"],
|
|
40
|
-
signedUrls: config.security?.signedUrls,
|
|
41
|
-
trash: config.security?.trash
|
|
42
|
-
},
|
|
43
|
-
information: config.information
|
|
44
|
-
};
|
|
45
|
-
return globalConfig;
|
|
46
|
-
}
|
|
47
|
-
};
|
|
48
|
-
var getDriveConfig = () => {
|
|
49
|
-
if (!globalConfig) throw new Error("Drive configuration not initialized");
|
|
50
|
-
return globalConfig;
|
|
51
|
-
};
|
|
52
|
-
var getDriveInformation = async (req) => {
|
|
53
|
-
const config = getDriveConfig();
|
|
54
|
-
if (config.mode === "ROOT") {
|
|
55
|
-
if (!config.information) {
|
|
56
|
-
return {
|
|
57
|
-
key: null,
|
|
58
|
-
storage: { quotaInBytes: Number.MAX_SAFE_INTEGER }
|
|
59
|
-
// Unlimited quota in ROOT mode
|
|
60
|
-
};
|
|
61
|
-
}
|
|
62
|
-
return config.information(req);
|
|
63
|
-
}
|
|
64
|
-
return config.information(req);
|
|
65
|
-
};
|
|
66
13
|
var informationSchema = new Schema({
|
|
67
14
|
type: { type: String, enum: ["FILE", "FOLDER"], required: true },
|
|
68
15
|
sizeInBytes: { type: Number, default: 0 },
|
|
@@ -115,8 +62,214 @@ DriveSchema.method("toClient", async function() {
|
|
|
115
62
|
createdAt: data.createdAt
|
|
116
63
|
};
|
|
117
64
|
});
|
|
118
|
-
var Drive =
|
|
65
|
+
var Drive = mongoose.models.Drive || mongoose.model("Drive", DriveSchema);
|
|
119
66
|
var drive_default = Drive;
|
|
67
|
+
|
|
68
|
+
// src/server/utils/migration.ts
|
|
69
|
+
var MIGRATION_FILE = ".migration-version";
|
|
70
|
+
var CURRENT_VERSION = 1;
|
|
71
|
+
var migrations = [
|
|
72
|
+
{
|
|
73
|
+
version: 1,
|
|
74
|
+
name: "restructure-file-paths",
|
|
75
|
+
migrate: async (storagePath) => {
|
|
76
|
+
const fileDir = path.join(storagePath, "file");
|
|
77
|
+
if (!fs.existsSync(fileDir)) {
|
|
78
|
+
fs.mkdirSync(fileDir, { recursive: true });
|
|
79
|
+
}
|
|
80
|
+
const files = await drive_default.find({
|
|
81
|
+
"provider.type": "LOCAL",
|
|
82
|
+
"information.type": "FILE",
|
|
83
|
+
"information.path": { $exists: true, $ne: "" }
|
|
84
|
+
}).lean();
|
|
85
|
+
for (const file of files) {
|
|
86
|
+
const info = file.information;
|
|
87
|
+
const oldPath = info.path;
|
|
88
|
+
if (!oldPath) continue;
|
|
89
|
+
if (oldPath.startsWith("file/")) continue;
|
|
90
|
+
const fileId = String(file._id);
|
|
91
|
+
const newRelativePath = path.join("file", fileId, "data.bin");
|
|
92
|
+
const oldFullPath = path.join(storagePath, oldPath);
|
|
93
|
+
const newFullPath = path.join(storagePath, newRelativePath);
|
|
94
|
+
if (fs.existsSync(oldFullPath)) {
|
|
95
|
+
const newDir = path.dirname(newFullPath);
|
|
96
|
+
if (!fs.existsSync(newDir)) {
|
|
97
|
+
fs.mkdirSync(newDir, { recursive: true });
|
|
98
|
+
}
|
|
99
|
+
try {
|
|
100
|
+
fs.renameSync(oldFullPath, newFullPath);
|
|
101
|
+
} catch (err) {
|
|
102
|
+
if (err instanceof Error && "code" in err && err.code === "EXDEV") {
|
|
103
|
+
fs.copyFileSync(oldFullPath, newFullPath);
|
|
104
|
+
fs.unlinkSync(oldFullPath);
|
|
105
|
+
} else {
|
|
106
|
+
console.error(`[next-drive] Migration failed for file ${fileId}:`, err);
|
|
107
|
+
continue;
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
const oldDir = path.dirname(oldFullPath);
|
|
111
|
+
try {
|
|
112
|
+
const remaining = fs.readdirSync(oldDir);
|
|
113
|
+
if (remaining.length === 0) {
|
|
114
|
+
fs.rmdirSync(oldDir);
|
|
115
|
+
}
|
|
116
|
+
} catch {
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
await drive_default.updateOne(
|
|
120
|
+
{ _id: file._id },
|
|
121
|
+
{ $set: { "information.path": newRelativePath } }
|
|
122
|
+
);
|
|
123
|
+
}
|
|
124
|
+
const oldCacheDir = path.join(storagePath, "cache", "thumbnails");
|
|
125
|
+
if (fs.existsSync(oldCacheDir)) {
|
|
126
|
+
const thumbnails = fs.readdirSync(oldCacheDir);
|
|
127
|
+
for (const thumb of thumbnails) {
|
|
128
|
+
const fileId = thumb.replace(".webp", "");
|
|
129
|
+
const oldThumbPath = path.join(oldCacheDir, thumb);
|
|
130
|
+
const newThumbDir = path.join(storagePath, "file", fileId, "cache");
|
|
131
|
+
const newThumbPath = path.join(newThumbDir, "thumbnail.webp");
|
|
132
|
+
if (fs.existsSync(oldThumbPath) && !fs.existsSync(newThumbPath)) {
|
|
133
|
+
if (!fs.existsSync(newThumbDir)) {
|
|
134
|
+
fs.mkdirSync(newThumbDir, { recursive: true });
|
|
135
|
+
}
|
|
136
|
+
try {
|
|
137
|
+
fs.renameSync(oldThumbPath, newThumbPath);
|
|
138
|
+
} catch {
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
try {
|
|
143
|
+
fs.rmSync(oldCacheDir, { recursive: true, force: true });
|
|
144
|
+
const cacheParent = path.join(storagePath, "cache");
|
|
145
|
+
const remaining = fs.readdirSync(cacheParent);
|
|
146
|
+
if (remaining.length === 0) {
|
|
147
|
+
fs.rmdirSync(cacheParent);
|
|
148
|
+
}
|
|
149
|
+
} catch {
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
const oldLibraryDir = path.join(storagePath, "library", "google");
|
|
153
|
+
if (fs.existsSync(oldLibraryDir)) {
|
|
154
|
+
const cachedFiles = fs.readdirSync(oldLibraryDir);
|
|
155
|
+
for (const cached of cachedFiles) {
|
|
156
|
+
const fileId = cached.split(".")[0];
|
|
157
|
+
if (!fileId) continue;
|
|
158
|
+
const oldCachedPath = path.join(oldLibraryDir, cached);
|
|
159
|
+
const newCachedDir = path.join(storagePath, "file", fileId);
|
|
160
|
+
const newCachedPath = path.join(newCachedDir, "data.bin");
|
|
161
|
+
if (fs.existsSync(oldCachedPath) && !fs.existsSync(newCachedPath)) {
|
|
162
|
+
if (!fs.existsSync(newCachedDir)) {
|
|
163
|
+
fs.mkdirSync(newCachedDir, { recursive: true });
|
|
164
|
+
}
|
|
165
|
+
try {
|
|
166
|
+
fs.renameSync(oldCachedPath, newCachedPath);
|
|
167
|
+
} catch {
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
try {
|
|
172
|
+
fs.rmSync(path.join(storagePath, "library"), { recursive: true, force: true });
|
|
173
|
+
} catch {
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
const oldDriveDir = path.join(storagePath, "drive");
|
|
177
|
+
if (fs.existsSync(oldDriveDir)) {
|
|
178
|
+
try {
|
|
179
|
+
fs.rmSync(oldDriveDir, { recursive: true, force: true });
|
|
180
|
+
} catch {
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
console.log("[next-drive] Migration v1 complete: restructure-file-paths");
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
];
|
|
187
|
+
var runMigrations = async (storagePath) => {
|
|
188
|
+
const versionFile = path.join(storagePath, MIGRATION_FILE);
|
|
189
|
+
let currentVersion = 0;
|
|
190
|
+
if (fs.existsSync(versionFile)) {
|
|
191
|
+
try {
|
|
192
|
+
currentVersion = parseInt(fs.readFileSync(versionFile, "utf-8").trim(), 10) || 0;
|
|
193
|
+
} catch {
|
|
194
|
+
currentVersion = 0;
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
if (currentVersion >= CURRENT_VERSION) return;
|
|
198
|
+
if (!fs.existsSync(storagePath)) {
|
|
199
|
+
fs.mkdirSync(storagePath, { recursive: true });
|
|
200
|
+
}
|
|
201
|
+
const pendingMigrations = migrations.filter((m) => m.version > currentVersion);
|
|
202
|
+
for (const migration of pendingMigrations.sort((a, b) => a.version - b.version)) {
|
|
203
|
+
console.log(`[next-drive] Running migration v${migration.version}: ${migration.name}`);
|
|
204
|
+
try {
|
|
205
|
+
await migration.migrate(storagePath);
|
|
206
|
+
fs.writeFileSync(versionFile, String(migration.version));
|
|
207
|
+
} catch (error) {
|
|
208
|
+
console.error(`[next-drive] Migration v${migration.version} failed:`, error);
|
|
209
|
+
throw error;
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
};
|
|
213
|
+
|
|
214
|
+
// src/server/config.ts
|
|
215
|
+
var globalConfig = null;
|
|
216
|
+
var migrationRun = false;
|
|
217
|
+
var driveConfiguration = async (config) => {
|
|
218
|
+
if (mongoose.connection.readyState !== 1) {
|
|
219
|
+
throw new Error("Database not connected. Please connect to Mongoose before initializing next-drive.");
|
|
220
|
+
}
|
|
221
|
+
if (!migrationRun) {
|
|
222
|
+
await runMigrations(config.storage.path);
|
|
223
|
+
migrationRun = true;
|
|
224
|
+
}
|
|
225
|
+
const mode = config.mode || "NORMAL";
|
|
226
|
+
if (mode === "ROOT") {
|
|
227
|
+
globalConfig = {
|
|
228
|
+
...config,
|
|
229
|
+
mode: "ROOT",
|
|
230
|
+
security: config.security || {
|
|
231
|
+
maxUploadSizeInBytes: 1024 * 1024 * 1024 * 10,
|
|
232
|
+
// 10GB default for ROOT
|
|
233
|
+
allowedMimeTypes: ["*/*"]
|
|
234
|
+
}
|
|
235
|
+
};
|
|
236
|
+
return globalConfig;
|
|
237
|
+
} else {
|
|
238
|
+
if (!config.information) {
|
|
239
|
+
throw new Error("information callback is required in NORMAL mode");
|
|
240
|
+
}
|
|
241
|
+
globalConfig = {
|
|
242
|
+
...config,
|
|
243
|
+
mode: "NORMAL",
|
|
244
|
+
security: {
|
|
245
|
+
maxUploadSizeInBytes: config.security?.maxUploadSizeInBytes ?? 10 * 1024 * 1024,
|
|
246
|
+
allowedMimeTypes: config.security?.allowedMimeTypes ?? ["*/*"],
|
|
247
|
+
signedUrls: config.security?.signedUrls,
|
|
248
|
+
trash: config.security?.trash
|
|
249
|
+
},
|
|
250
|
+
information: config.information
|
|
251
|
+
};
|
|
252
|
+
return globalConfig;
|
|
253
|
+
}
|
|
254
|
+
};
|
|
255
|
+
var getDriveConfig = () => {
|
|
256
|
+
if (!globalConfig) throw new Error("Drive configuration not initialized");
|
|
257
|
+
return globalConfig;
|
|
258
|
+
};
|
|
259
|
+
var getDriveInformation = async (req) => {
|
|
260
|
+
const config = getDriveConfig();
|
|
261
|
+
if (config.mode === "ROOT") {
|
|
262
|
+
if (!config.information) {
|
|
263
|
+
return {
|
|
264
|
+
key: null,
|
|
265
|
+
storage: { quotaInBytes: Number.MAX_SAFE_INTEGER }
|
|
266
|
+
// Unlimited quota in ROOT mode
|
|
267
|
+
};
|
|
268
|
+
}
|
|
269
|
+
return config.information(req);
|
|
270
|
+
}
|
|
271
|
+
return config.information(req);
|
|
272
|
+
};
|
|
120
273
|
var StorageAccountSchema = new Schema(
|
|
121
274
|
{
|
|
122
275
|
owner: { type: Schema.Types.Mixed, default: null },
|
|
@@ -144,7 +297,7 @@ StorageAccountSchema.method("toClient", async function() {
|
|
|
144
297
|
createdAt: data.createdAt
|
|
145
298
|
};
|
|
146
299
|
});
|
|
147
|
-
var StorageAccount =
|
|
300
|
+
var StorageAccount = mongoose.models.StorageAccount || mongoose.model("StorageAccount", StorageAccountSchema);
|
|
148
301
|
var account_default = StorageAccount;
|
|
149
302
|
var validateMimeType = (mime, allowedTypes) => {
|
|
150
303
|
if (allowedTypes.includes("*/*")) return true;
|
|
@@ -159,7 +312,7 @@ var validateMimeType = (mime, allowedTypes) => {
|
|
|
159
312
|
};
|
|
160
313
|
var computeFileHash = (filePath) => new Promise((resolve, reject) => {
|
|
161
314
|
const hash = crypto2.createHash("sha256");
|
|
162
|
-
const stream =
|
|
315
|
+
const stream = fs.createReadStream(filePath);
|
|
163
316
|
stream.on("data", (data) => hash.update(data));
|
|
164
317
|
stream.on("end", () => resolve(hash.digest("hex")));
|
|
165
318
|
stream.on("error", reject);
|
|
@@ -203,9 +356,7 @@ var listQuerySchema = z.object({
|
|
|
203
356
|
});
|
|
204
357
|
z.object({
|
|
205
358
|
id: objectIdSchema,
|
|
206
|
-
token: z.string().optional()
|
|
207
|
-
q: z.enum(["ultralow", "low", "medium", "high", "normal"]).optional(),
|
|
208
|
-
format: z.enum(["webp", "jpeg", "png"]).optional()
|
|
359
|
+
token: z.string().optional()
|
|
209
360
|
});
|
|
210
361
|
z.object({
|
|
211
362
|
id: objectIdSchema,
|
|
@@ -295,12 +446,13 @@ var LocalStorageProvider = {
|
|
|
295
446
|
},
|
|
296
447
|
openStream: async (item, accountId) => {
|
|
297
448
|
if (item.information.type !== "FILE") throw new Error("Cannot stream folder");
|
|
298
|
-
const
|
|
299
|
-
|
|
449
|
+
const storagePath = getDriveConfig().storage.path;
|
|
450
|
+
const filePath = path.join(storagePath, "file", item._id.toString(), "data.bin");
|
|
451
|
+
if (!fs.existsSync(filePath)) {
|
|
300
452
|
throw new Error("File not found on disk");
|
|
301
453
|
}
|
|
302
|
-
const stat =
|
|
303
|
-
const stream =
|
|
454
|
+
const stat = fs.statSync(filePath);
|
|
455
|
+
const stream = fs.createReadStream(filePath);
|
|
304
456
|
return {
|
|
305
457
|
stream,
|
|
306
458
|
mime: item.information.mime,
|
|
@@ -310,28 +462,30 @@ var LocalStorageProvider = {
|
|
|
310
462
|
getThumbnail: async (item, accountId) => {
|
|
311
463
|
if (item.information.type !== "FILE") throw new Error("No thumbnail for folder");
|
|
312
464
|
const storagePath = getDriveConfig().storage.path;
|
|
313
|
-
const
|
|
314
|
-
const
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
465
|
+
const fileDir = path.join(storagePath, "file", item._id.toString());
|
|
466
|
+
const originalPath = path.join(fileDir, "data.bin");
|
|
467
|
+
const thumbDir = path.join(fileDir, "cache");
|
|
468
|
+
const thumbPath = path.join(thumbDir, "thumbnail.webp");
|
|
469
|
+
if (!fs.existsSync(originalPath)) throw new Error("Original file not found");
|
|
470
|
+
if (fs.existsSync(thumbPath)) {
|
|
471
|
+
return fs.createReadStream(thumbPath);
|
|
318
472
|
}
|
|
319
|
-
if (!
|
|
473
|
+
if (!fs.existsSync(thumbDir)) fs.mkdirSync(thumbDir, { recursive: true });
|
|
320
474
|
if (item.information.mime.startsWith("image/")) {
|
|
321
475
|
await sharp(originalPath).resize(300, 300, { fit: "inside" }).toFormat("webp", { quality: 80 }).toFile(thumbPath);
|
|
322
476
|
} else if (item.information.mime.startsWith("video/")) {
|
|
323
477
|
await new Promise((resolve, reject) => {
|
|
324
478
|
ffmpeg(originalPath).screenshots({
|
|
325
479
|
count: 1,
|
|
326
|
-
folder:
|
|
327
|
-
filename:
|
|
480
|
+
folder: path.dirname(thumbPath),
|
|
481
|
+
filename: path.basename(thumbPath),
|
|
328
482
|
size: "300x?"
|
|
329
483
|
}).on("end", resolve).on("error", reject);
|
|
330
484
|
});
|
|
331
485
|
} else {
|
|
332
486
|
throw new Error("Unsupported mime type for thumbnail");
|
|
333
487
|
}
|
|
334
|
-
return
|
|
488
|
+
return fs.createReadStream(thumbPath);
|
|
335
489
|
},
|
|
336
490
|
createFolder: async (name, parentId, owner, accountId) => {
|
|
337
491
|
const getNextOrderValue2 = async (owner2) => {
|
|
@@ -353,33 +507,34 @@ var LocalStorageProvider = {
|
|
|
353
507
|
uploadFile: async (drive, filePath, accountId) => {
|
|
354
508
|
if (drive.information.type !== "FILE") throw new Error("Invalid drive type");
|
|
355
509
|
const storagePath = getDriveConfig().storage.path;
|
|
356
|
-
const
|
|
357
|
-
const
|
|
358
|
-
if (!
|
|
510
|
+
const destDir = path.join(storagePath, "file", String(drive._id));
|
|
511
|
+
const destPath = path.join(destDir, "data.bin");
|
|
512
|
+
if (!fs.existsSync(filePath)) {
|
|
359
513
|
throw new Error("Source file not found");
|
|
360
514
|
}
|
|
361
|
-
if (!
|
|
362
|
-
|
|
515
|
+
if (!fs.existsSync(destDir)) {
|
|
516
|
+
fs.mkdirSync(destDir, { recursive: true });
|
|
363
517
|
}
|
|
364
518
|
try {
|
|
365
|
-
|
|
519
|
+
fs.renameSync(filePath, destPath);
|
|
366
520
|
} catch (err) {
|
|
367
521
|
if (err instanceof Error && "code" in err && err.code === "EXDEV") {
|
|
368
|
-
|
|
369
|
-
|
|
522
|
+
fs.copyFileSync(filePath, destPath);
|
|
523
|
+
fs.unlinkSync(filePath);
|
|
370
524
|
} else {
|
|
371
525
|
throw err;
|
|
372
526
|
}
|
|
373
527
|
}
|
|
374
|
-
if (!
|
|
528
|
+
if (!fs.existsSync(destPath)) {
|
|
375
529
|
throw new Error("Failed to write file to destination");
|
|
376
530
|
}
|
|
377
|
-
const destStats =
|
|
531
|
+
const destStats = fs.statSync(destPath);
|
|
378
532
|
if (destStats.size !== drive.information.sizeInBytes) {
|
|
379
|
-
|
|
533
|
+
fs.unlinkSync(destPath);
|
|
380
534
|
throw new Error(`Destination file size mismatch: expected ${drive.information.sizeInBytes}, got ${destStats.size}`);
|
|
381
535
|
}
|
|
382
536
|
drive.status = "READY";
|
|
537
|
+
drive.information.path = path.join("file", String(drive._id), "data.bin");
|
|
383
538
|
drive.information.hash = await computeFileHash(destPath);
|
|
384
539
|
if (drive.information.mime.startsWith("image/")) {
|
|
385
540
|
const meta = await extractImageMetadata(destPath);
|
|
@@ -405,10 +560,9 @@ var LocalStorageProvider = {
|
|
|
405
560
|
const allItemsToDelete = [...items, ...allChildren];
|
|
406
561
|
for (const item of allItemsToDelete) {
|
|
407
562
|
if (item.information.type === "FILE" && item.information.path) {
|
|
408
|
-
const
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
fs4.rmSync(dirPath, { recursive: true, force: true });
|
|
563
|
+
const fileDir = path.join(getDriveConfig().storage.path, "file", item._id.toString());
|
|
564
|
+
if (fs.existsSync(fileDir)) {
|
|
565
|
+
fs.rmSync(fileDir, { recursive: true, force: true });
|
|
412
566
|
}
|
|
413
567
|
}
|
|
414
568
|
}
|
|
@@ -428,8 +582,7 @@ var LocalStorageProvider = {
|
|
|
428
582
|
move: async (id, newParentId, owner, accountId) => {
|
|
429
583
|
const item = await drive_default.findOne({ _id: id, owner });
|
|
430
584
|
if (!item) throw new Error("Item not found");
|
|
431
|
-
item.parentId;
|
|
432
|
-
item.parentId = newParentId === "root" || !newParentId ? null : new mongoose2.Types.ObjectId(newParentId);
|
|
585
|
+
item.parentId = newParentId === "root" || !newParentId ? null : new mongoose.Types.ObjectId(newParentId);
|
|
433
586
|
await item.save();
|
|
434
587
|
return item.toClient();
|
|
435
588
|
},
|
|
@@ -472,17 +625,19 @@ var GoogleDriveProvider = {
|
|
|
472
625
|
return;
|
|
473
626
|
}
|
|
474
627
|
}
|
|
475
|
-
let
|
|
628
|
+
let pageToken = void 0;
|
|
476
629
|
const allSyncedGoogleIds = /* @__PURE__ */ new Set();
|
|
477
630
|
do {
|
|
478
|
-
const
|
|
631
|
+
const listParams = {
|
|
479
632
|
q: `'${googleParentId}' in parents and trashed = false`,
|
|
480
633
|
fields: "nextPageToken, files(id, name, mimeType, size, webViewLink, iconLink, thumbnailLink, createdTime)",
|
|
481
634
|
pageSize: 1e3,
|
|
482
|
-
pageToken
|
|
483
|
-
}
|
|
484
|
-
|
|
485
|
-
const
|
|
635
|
+
pageToken
|
|
636
|
+
};
|
|
637
|
+
const res = await drive.files.list(listParams);
|
|
638
|
+
const responseData = res.data;
|
|
639
|
+
pageToken = responseData.nextPageToken ?? void 0;
|
|
640
|
+
const files = responseData.files ?? [];
|
|
486
641
|
for (const file of files) {
|
|
487
642
|
if (!file.id || !file.name || !file.mimeType) continue;
|
|
488
643
|
allSyncedGoogleIds.add(file.id);
|
|
@@ -521,7 +676,7 @@ var GoogleDriveProvider = {
|
|
|
521
676
|
{ upsert: true, new: true, setDefaultsOnInsert: true }
|
|
522
677
|
);
|
|
523
678
|
}
|
|
524
|
-
} while (
|
|
679
|
+
} while (pageToken);
|
|
525
680
|
const dbItems = await drive_default.find({
|
|
526
681
|
owner,
|
|
527
682
|
storageAccountId: foundAccountId,
|
|
@@ -537,18 +692,19 @@ var GoogleDriveProvider = {
|
|
|
537
692
|
},
|
|
538
693
|
syncTrash: async (owner, accountId) => {
|
|
539
694
|
const { client, accountId: foundAccountId } = await createAuthClient(owner, accountId);
|
|
540
|
-
const
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
const res = await drive.files.list({
|
|
695
|
+
const driveApi = google.drive({ version: "v3", auth: client });
|
|
696
|
+
const fetchTrashedFiles = async (pageToken) => {
|
|
697
|
+
return driveApi.files.list({
|
|
544
698
|
q: "trashed = true",
|
|
545
699
|
fields: "nextPageToken, files(id, name, mimeType, size, webViewLink, iconLink, thumbnailLink, createdTime)",
|
|
546
700
|
pageSize: 100,
|
|
547
|
-
|
|
548
|
-
pageToken: nextPageToken
|
|
701
|
+
pageToken
|
|
549
702
|
});
|
|
550
|
-
|
|
551
|
-
|
|
703
|
+
};
|
|
704
|
+
let nextToken = void 0;
|
|
705
|
+
do {
|
|
706
|
+
const listResult = await fetchTrashedFiles(nextToken);
|
|
707
|
+
const files = listResult.data.files || [];
|
|
552
708
|
for (const file of files) {
|
|
553
709
|
if (!file.id || !file.name || !file.mimeType) continue;
|
|
554
710
|
const isFolder = file.mimeType === "application/vnd.google-apps.folder";
|
|
@@ -582,7 +738,8 @@ var GoogleDriveProvider = {
|
|
|
582
738
|
{ upsert: true, setDefaultsOnInsert: true }
|
|
583
739
|
);
|
|
584
740
|
}
|
|
585
|
-
|
|
741
|
+
nextToken = listResult.data.nextPageToken ?? void 0;
|
|
742
|
+
} while (nextToken);
|
|
586
743
|
},
|
|
587
744
|
search: async (query, owner, accountId) => {
|
|
588
745
|
const { client, accountId: foundAccountId } = await createAuthClient(owner, accountId);
|
|
@@ -666,7 +823,10 @@ var GoogleDriveProvider = {
|
|
|
666
823
|
const drive = google.drive({ version: "v3", auth: client });
|
|
667
824
|
if (!item.provider?.google?.id) throw new Error("Missing Google File ID");
|
|
668
825
|
if (item.information.type === "FOLDER") throw new Error("Cannot stream folder");
|
|
669
|
-
const res = await drive.files.get(
|
|
826
|
+
const res = await drive.files.get(
|
|
827
|
+
{ fileId: item.provider.google.id, alt: "media" },
|
|
828
|
+
{ responseType: "stream" }
|
|
829
|
+
);
|
|
670
830
|
return {
|
|
671
831
|
stream: res.data,
|
|
672
832
|
mime: item.information.mime,
|
|
@@ -674,10 +834,36 @@ var GoogleDriveProvider = {
|
|
|
674
834
|
};
|
|
675
835
|
},
|
|
676
836
|
getThumbnail: async (item, accountId) => {
|
|
837
|
+
const config = getDriveConfig();
|
|
838
|
+
const storagePath = config.storage.path;
|
|
839
|
+
const thumbDir = path.join(storagePath, "file", item._id.toString(), "cache");
|
|
840
|
+
const thumbPath = path.join(thumbDir, "thumbnail.webp");
|
|
841
|
+
if (fs.existsSync(thumbPath)) {
|
|
842
|
+
return fs.createReadStream(thumbPath);
|
|
843
|
+
}
|
|
677
844
|
const { client } = await createAuthClient(item.owner, accountId || item.storageAccountId?.toString());
|
|
678
845
|
if (!item.provider?.google?.thumbnailLink) throw new Error("No thumbnail available");
|
|
679
846
|
const res = await client.request({ url: item.provider.google.thumbnailLink, responseType: "stream" });
|
|
680
|
-
|
|
847
|
+
if (!fs.existsSync(thumbDir)) {
|
|
848
|
+
fs.mkdirSync(thumbDir, { recursive: true });
|
|
849
|
+
}
|
|
850
|
+
const tempPath = `${thumbPath}.tmp`;
|
|
851
|
+
const writeStream = fs.createWriteStream(tempPath);
|
|
852
|
+
await new Promise((resolve, reject) => {
|
|
853
|
+
res.data.pipe(writeStream);
|
|
854
|
+
writeStream.on("finish", resolve);
|
|
855
|
+
writeStream.on("error", reject);
|
|
856
|
+
});
|
|
857
|
+
try {
|
|
858
|
+
fs.renameSync(tempPath, thumbPath);
|
|
859
|
+
} catch {
|
|
860
|
+
fs.unlinkSync(tempPath);
|
|
861
|
+
}
|
|
862
|
+
if (fs.existsSync(thumbPath)) {
|
|
863
|
+
return fs.createReadStream(thumbPath);
|
|
864
|
+
}
|
|
865
|
+
const refetch = await client.request({ url: item.provider.google.thumbnailLink, responseType: "stream" });
|
|
866
|
+
return refetch.data;
|
|
681
867
|
},
|
|
682
868
|
createFolder: async (name, parentId, owner, accountId) => {
|
|
683
869
|
const { client, accountId: foundAccountId } = await createAuthClient(owner, accountId);
|
|
@@ -734,7 +920,7 @@ var GoogleDriveProvider = {
|
|
|
734
920
|
},
|
|
735
921
|
media: {
|
|
736
922
|
mimeType: drive.information.mime,
|
|
737
|
-
body:
|
|
923
|
+
body: fs.createReadStream(filePath)
|
|
738
924
|
},
|
|
739
925
|
fields: "id, name, mimeType, webViewLink, iconLink, thumbnailLink, size"
|
|
740
926
|
});
|
|
@@ -752,7 +938,7 @@ var GoogleDriveProvider = {
|
|
|
752
938
|
};
|
|
753
939
|
} catch (error) {
|
|
754
940
|
drive.status = "FAILED";
|
|
755
|
-
console.error("Google Upload Error:", error);
|
|
941
|
+
console.error("[next-drive] Google Upload Error:", error);
|
|
756
942
|
throw error;
|
|
757
943
|
}
|
|
758
944
|
await drive.save();
|
|
@@ -853,7 +1039,7 @@ var GoogleDriveProvider = {
|
|
|
853
1039
|
removeParents: previousGoogleParentId,
|
|
854
1040
|
fields: "id, parents"
|
|
855
1041
|
});
|
|
856
|
-
item.parentId = newParentId === "root" || !newParentId ? null : new
|
|
1042
|
+
item.parentId = newParentId === "root" || !newParentId ? null : new mongoose.Types.ObjectId(newParentId);
|
|
857
1043
|
await item.save();
|
|
858
1044
|
return item.toClient();
|
|
859
1045
|
},
|
|
@@ -863,7 +1049,7 @@ var GoogleDriveProvider = {
|
|
|
863
1049
|
const account = await account_default.findById(accountId);
|
|
864
1050
|
if (account?.metadata?.provider === "GOOGLE" && account.metadata.google?.credentials) {
|
|
865
1051
|
const creds = account.metadata.google.credentials;
|
|
866
|
-
if (typeof creds === "object" && "access_token" in creds) {
|
|
1052
|
+
if (typeof creds === "object" && "access_token" in creds && typeof creds.access_token === "string") {
|
|
867
1053
|
await client.revokeToken(creds.access_token);
|
|
868
1054
|
}
|
|
869
1055
|
}
|
|
@@ -965,8 +1151,8 @@ var driveFilePath = async (file) => {
|
|
|
965
1151
|
const STORAGE_PATH = config.storage.path;
|
|
966
1152
|
const providerType = drive.provider?.type || "LOCAL";
|
|
967
1153
|
if (providerType === "LOCAL") {
|
|
968
|
-
const filePath =
|
|
969
|
-
if (!
|
|
1154
|
+
const filePath = path.join(STORAGE_PATH, "file", String(drive._id), "data.bin");
|
|
1155
|
+
if (!fs.existsSync(filePath)) {
|
|
970
1156
|
throw new Error(`Local file not found on disk: ${filePath}`);
|
|
971
1157
|
}
|
|
972
1158
|
return Object.freeze({
|
|
@@ -978,11 +1164,10 @@ var driveFilePath = async (file) => {
|
|
|
978
1164
|
});
|
|
979
1165
|
}
|
|
980
1166
|
if (providerType === "GOOGLE") {
|
|
981
|
-
const
|
|
982
|
-
const
|
|
983
|
-
|
|
984
|
-
|
|
985
|
-
const stats = fs4.statSync(cachedFilePath);
|
|
1167
|
+
const fileDir = path.join(STORAGE_PATH, "file", String(drive._id));
|
|
1168
|
+
const cachedFilePath = path.join(fileDir, "data.bin");
|
|
1169
|
+
if (fs.existsSync(cachedFilePath)) {
|
|
1170
|
+
const stats = fs.statSync(cachedFilePath);
|
|
986
1171
|
if (stats.size === drive.information.sizeInBytes) {
|
|
987
1172
|
return Object.freeze({
|
|
988
1173
|
path: cachedFilePath,
|
|
@@ -992,15 +1177,15 @@ var driveFilePath = async (file) => {
|
|
|
992
1177
|
provider: "GOOGLE"
|
|
993
1178
|
});
|
|
994
1179
|
}
|
|
995
|
-
|
|
1180
|
+
fs.unlinkSync(cachedFilePath);
|
|
996
1181
|
}
|
|
997
1182
|
const accountId = drive.storageAccountId?.toString();
|
|
998
1183
|
const { stream } = await GoogleDriveProvider.openStream(drive, accountId);
|
|
999
|
-
if (!
|
|
1000
|
-
|
|
1184
|
+
if (!fs.existsSync(fileDir)) {
|
|
1185
|
+
fs.mkdirSync(fileDir, { recursive: true });
|
|
1001
1186
|
}
|
|
1002
1187
|
const tempPath = `${cachedFilePath}.tmp`;
|
|
1003
|
-
const writeStream =
|
|
1188
|
+
const writeStream = fs.createWriteStream(tempPath);
|
|
1004
1189
|
await new Promise((resolve, reject) => {
|
|
1005
1190
|
stream.pipe(writeStream);
|
|
1006
1191
|
writeStream.on("finish", resolve);
|
|
@@ -1008,11 +1193,11 @@ var driveFilePath = async (file) => {
|
|
|
1008
1193
|
stream.on("error", reject);
|
|
1009
1194
|
});
|
|
1010
1195
|
try {
|
|
1011
|
-
|
|
1196
|
+
fs.renameSync(tempPath, cachedFilePath);
|
|
1012
1197
|
} catch (err) {
|
|
1013
1198
|
if (err instanceof Error && "code" in err && err.code === "EXDEV") {
|
|
1014
|
-
|
|
1015
|
-
|
|
1199
|
+
fs.copyFileSync(tempPath, cachedFilePath);
|
|
1200
|
+
fs.unlinkSync(tempPath);
|
|
1016
1201
|
} else {
|
|
1017
1202
|
throw err;
|
|
1018
1203
|
}
|
|
@@ -1103,28 +1288,28 @@ var driveUpload = async (source, key, options) => {
|
|
|
1103
1288
|
let sourceFilePath;
|
|
1104
1289
|
let fileSize;
|
|
1105
1290
|
if (typeof source === "string") {
|
|
1106
|
-
if (!
|
|
1291
|
+
if (!fs.existsSync(source)) {
|
|
1107
1292
|
throw new Error(`Source file not found: ${source}`);
|
|
1108
1293
|
}
|
|
1109
1294
|
sourceFilePath = source;
|
|
1110
|
-
const stats =
|
|
1295
|
+
const stats = fs.statSync(source);
|
|
1111
1296
|
fileSize = stats.size;
|
|
1112
1297
|
} else if (Buffer.isBuffer(source)) {
|
|
1113
|
-
const tempDir =
|
|
1114
|
-
if (!
|
|
1115
|
-
|
|
1298
|
+
const tempDir = path.join(os2.tmpdir(), "next-drive-uploads");
|
|
1299
|
+
if (!fs.existsSync(tempDir)) {
|
|
1300
|
+
fs.mkdirSync(tempDir, { recursive: true });
|
|
1116
1301
|
}
|
|
1117
|
-
tempFilePath =
|
|
1118
|
-
|
|
1302
|
+
tempFilePath = path.join(tempDir, `upload-${crypto2.randomUUID()}.tmp`);
|
|
1303
|
+
fs.writeFileSync(tempFilePath, source);
|
|
1119
1304
|
sourceFilePath = tempFilePath;
|
|
1120
1305
|
fileSize = source.length;
|
|
1121
1306
|
} else {
|
|
1122
|
-
const tempDir =
|
|
1123
|
-
if (!
|
|
1124
|
-
|
|
1307
|
+
const tempDir = path.join(os2.tmpdir(), "next-drive-uploads");
|
|
1308
|
+
if (!fs.existsSync(tempDir)) {
|
|
1309
|
+
fs.mkdirSync(tempDir, { recursive: true });
|
|
1125
1310
|
}
|
|
1126
|
-
tempFilePath =
|
|
1127
|
-
const writeStream =
|
|
1311
|
+
tempFilePath = path.join(tempDir, `upload-${crypto2.randomUUID()}.tmp`);
|
|
1312
|
+
const writeStream = fs.createWriteStream(tempFilePath);
|
|
1128
1313
|
await new Promise((resolve, reject) => {
|
|
1129
1314
|
source.pipe(writeStream);
|
|
1130
1315
|
writeStream.on("finish", resolve);
|
|
@@ -1132,7 +1317,7 @@ var driveUpload = async (source, key, options) => {
|
|
|
1132
1317
|
source.on("error", reject);
|
|
1133
1318
|
});
|
|
1134
1319
|
sourceFilePath = tempFilePath;
|
|
1135
|
-
const stats =
|
|
1320
|
+
const stats = fs.statSync(tempFilePath);
|
|
1136
1321
|
fileSize = stats.size;
|
|
1137
1322
|
}
|
|
1138
1323
|
try {
|
|
@@ -1140,7 +1325,7 @@ var driveUpload = async (source, key, options) => {
|
|
|
1140
1325
|
if (options.mime) {
|
|
1141
1326
|
mimeType = options.mime;
|
|
1142
1327
|
} else {
|
|
1143
|
-
const ext =
|
|
1328
|
+
const ext = path.extname(options.name).toLowerCase();
|
|
1144
1329
|
const mimeTypes = {
|
|
1145
1330
|
".jpg": "image/jpeg",
|
|
1146
1331
|
".jpeg": "image/jpeg",
|
|
@@ -1186,10 +1371,7 @@ var driveUpload = async (source, key, options) => {
|
|
|
1186
1371
|
status: "UPLOADING"
|
|
1187
1372
|
});
|
|
1188
1373
|
if (provider.name === "LOCAL" && drive.information.type === "FILE") {
|
|
1189
|
-
|
|
1190
|
-
sanitizedExt = sanitizedExt.replace(/[^a-zA-Z0-9.]/g, "").slice(0, 11);
|
|
1191
|
-
if (!sanitizedExt.startsWith(".")) sanitizedExt = ".bin";
|
|
1192
|
-
drive.information.path = path3.join(String(drive._id), `data${sanitizedExt}`);
|
|
1374
|
+
drive.information.path = path.join("file", String(drive._id), "data.bin");
|
|
1193
1375
|
}
|
|
1194
1376
|
await drive.save();
|
|
1195
1377
|
try {
|
|
@@ -1207,8 +1389,8 @@ var driveUpload = async (source, key, options) => {
|
|
|
1207
1389
|
throw err;
|
|
1208
1390
|
}
|
|
1209
1391
|
} finally {
|
|
1210
|
-
if (tempFilePath &&
|
|
1211
|
-
|
|
1392
|
+
if (tempFilePath && fs.existsSync(tempFilePath)) {
|
|
1393
|
+
fs.rmSync(tempFilePath, { force: true });
|
|
1212
1394
|
}
|
|
1213
1395
|
}
|
|
1214
1396
|
};
|
|
@@ -1538,8 +1720,8 @@ var driveAPIHandler = async (req, res) => {
|
|
|
1538
1720
|
// ** 3. UPLOAD **
|
|
1539
1721
|
case "upload": {
|
|
1540
1722
|
if (req.method !== "POST") return res.status(405).json({ status: 405, message: "Only POST allowed" });
|
|
1541
|
-
const systemTmpDir =
|
|
1542
|
-
if (!
|
|
1723
|
+
const systemTmpDir = path.join(os2.tmpdir(), "next-drive-uploads");
|
|
1724
|
+
if (!fs.existsSync(systemTmpDir)) fs.mkdirSync(systemTmpDir, { recursive: true });
|
|
1543
1725
|
const form = formidable({
|
|
1544
1726
|
multiples: false,
|
|
1545
1727
|
maxFileSize: (config.security?.maxUploadSizeInBytes ?? 1024 * 1024 * 1024) * 2,
|
|
@@ -1554,7 +1736,7 @@ var driveAPIHandler = async (req, res) => {
|
|
|
1554
1736
|
});
|
|
1555
1737
|
const cleanupTempFiles = (files2) => {
|
|
1556
1738
|
Object.values(files2).flat().forEach((file) => {
|
|
1557
|
-
if (file &&
|
|
1739
|
+
if (file && fs.existsSync(file.filepath)) fs.rmSync(file.filepath, { force: true });
|
|
1558
1740
|
});
|
|
1559
1741
|
};
|
|
1560
1742
|
const getString = (f) => Array.isArray(f) ? f[0] : f || "";
|
|
@@ -1574,7 +1756,7 @@ var driveAPIHandler = async (req, res) => {
|
|
|
1574
1756
|
}
|
|
1575
1757
|
const { chunkIndex, totalChunks, driveId, fileName, fileSize: fileSizeInBytes, fileType, folderId } = uploadData.data;
|
|
1576
1758
|
let currentUploadId = driveId;
|
|
1577
|
-
const tempBaseDir =
|
|
1759
|
+
const tempBaseDir = path.join(os2.tmpdir(), "next-drive-uploads");
|
|
1578
1760
|
if (!currentUploadId) {
|
|
1579
1761
|
if (chunkIndex !== 0) return res.status(400).json({ message: "Missing upload ID for non-zero chunk" });
|
|
1580
1762
|
if (fileType && config.security) {
|
|
@@ -1591,8 +1773,8 @@ var driveAPIHandler = async (req, res) => {
|
|
|
1591
1773
|
}
|
|
1592
1774
|
}
|
|
1593
1775
|
currentUploadId = crypto2.randomUUID();
|
|
1594
|
-
const uploadDir =
|
|
1595
|
-
|
|
1776
|
+
const uploadDir = path.join(tempBaseDir, currentUploadId);
|
|
1777
|
+
fs.mkdirSync(uploadDir, { recursive: true });
|
|
1596
1778
|
const metadata = {
|
|
1597
1779
|
owner,
|
|
1598
1780
|
accountId,
|
|
@@ -1603,34 +1785,34 @@ var driveAPIHandler = async (req, res) => {
|
|
|
1603
1785
|
mimeType: fileType,
|
|
1604
1786
|
totalChunks
|
|
1605
1787
|
};
|
|
1606
|
-
|
|
1788
|
+
fs.writeFileSync(path.join(uploadDir, "metadata.json"), JSON.stringify(metadata));
|
|
1607
1789
|
}
|
|
1608
1790
|
if (currentUploadId) {
|
|
1609
|
-
const uploadDir =
|
|
1610
|
-
if (!
|
|
1791
|
+
const uploadDir = path.join(tempBaseDir, currentUploadId);
|
|
1792
|
+
if (!fs.existsSync(uploadDir)) {
|
|
1611
1793
|
cleanupTempFiles(files);
|
|
1612
1794
|
return res.status(404).json({ status: 404, message: "Upload session not found or expired" });
|
|
1613
1795
|
}
|
|
1614
1796
|
try {
|
|
1615
1797
|
const chunkFile = Array.isArray(files.chunk) ? files.chunk[0] : files.chunk;
|
|
1616
1798
|
if (!chunkFile) throw new Error("No chunk file received");
|
|
1617
|
-
const partPath =
|
|
1799
|
+
const partPath = path.join(uploadDir, `part_${chunkIndex}`);
|
|
1618
1800
|
try {
|
|
1619
|
-
|
|
1801
|
+
fs.renameSync(chunkFile.filepath, partPath);
|
|
1620
1802
|
} catch (err) {
|
|
1621
1803
|
if (err instanceof Error && "code" in err && err.code === "EXDEV") {
|
|
1622
|
-
|
|
1623
|
-
|
|
1804
|
+
fs.copyFileSync(chunkFile.filepath, partPath);
|
|
1805
|
+
fs.unlinkSync(chunkFile.filepath);
|
|
1624
1806
|
} else {
|
|
1625
1807
|
throw err;
|
|
1626
1808
|
}
|
|
1627
1809
|
}
|
|
1628
|
-
const uploadedParts =
|
|
1810
|
+
const uploadedParts = fs.readdirSync(uploadDir).filter((f) => f.startsWith("part_"));
|
|
1629
1811
|
if (uploadedParts.length === totalChunks) {
|
|
1630
|
-
const metaPath =
|
|
1631
|
-
const meta = JSON.parse(
|
|
1632
|
-
const finalTempPath =
|
|
1633
|
-
const writeStream =
|
|
1812
|
+
const metaPath = path.join(uploadDir, "metadata.json");
|
|
1813
|
+
const meta = JSON.parse(fs.readFileSync(metaPath, "utf-8"));
|
|
1814
|
+
const finalTempPath = path.join(uploadDir, "final.bin");
|
|
1815
|
+
const writeStream = fs.createWriteStream(finalTempPath);
|
|
1634
1816
|
let streamError = null;
|
|
1635
1817
|
writeStream.on("error", (err) => {
|
|
1636
1818
|
streamError = err;
|
|
@@ -1644,12 +1826,12 @@ var driveAPIHandler = async (req, res) => {
|
|
|
1644
1826
|
writeStream.destroy();
|
|
1645
1827
|
throw streamError;
|
|
1646
1828
|
}
|
|
1647
|
-
const pPath =
|
|
1648
|
-
if (!
|
|
1829
|
+
const pPath = path.join(uploadDir, `part_${i}`);
|
|
1830
|
+
if (!fs.existsSync(pPath)) {
|
|
1649
1831
|
writeStream.destroy();
|
|
1650
1832
|
throw new Error(`Missing chunk part: ${i}`);
|
|
1651
1833
|
}
|
|
1652
|
-
const data =
|
|
1834
|
+
const data = fs.readFileSync(pPath);
|
|
1653
1835
|
const canContinue = writeStream.write(data);
|
|
1654
1836
|
if (!canContinue) {
|
|
1655
1837
|
await new Promise((resolve, reject) => {
|
|
@@ -1667,10 +1849,10 @@ var driveAPIHandler = async (req, res) => {
|
|
|
1667
1849
|
writeStream.on("finish", resolve);
|
|
1668
1850
|
writeStream.once("error", reject);
|
|
1669
1851
|
});
|
|
1670
|
-
if (!
|
|
1852
|
+
if (!fs.existsSync(finalTempPath)) {
|
|
1671
1853
|
throw new Error("Failed to create merged file");
|
|
1672
1854
|
}
|
|
1673
|
-
const finalStats =
|
|
1855
|
+
const finalStats = fs.statSync(finalTempPath);
|
|
1674
1856
|
if (finalStats.size !== meta.fileSize) {
|
|
1675
1857
|
throw new Error(`File size mismatch: expected ${meta.fileSize}, got ${finalStats.size}`);
|
|
1676
1858
|
}
|
|
@@ -1688,15 +1870,12 @@ var driveAPIHandler = async (req, res) => {
|
|
|
1688
1870
|
totalChunks
|
|
1689
1871
|
});
|
|
1690
1872
|
if (meta.providerName === "LOCAL" && drive.information.type === "FILE") {
|
|
1691
|
-
|
|
1692
|
-
ext = ext.replace(/[^a-zA-Z0-9.]/g, "").slice(0, 11);
|
|
1693
|
-
if (!ext.startsWith(".")) ext = ".bin";
|
|
1694
|
-
drive.information.path = path3.join(String(drive._id), `data${ext}`);
|
|
1873
|
+
drive.information.path = path.join("file", String(drive._id), "data.bin");
|
|
1695
1874
|
}
|
|
1696
1875
|
await drive.save();
|
|
1697
1876
|
try {
|
|
1698
1877
|
const item = await provider.uploadFile(drive, finalTempPath, meta.accountId);
|
|
1699
|
-
|
|
1878
|
+
fs.rmSync(uploadDir, { recursive: true, force: true });
|
|
1700
1879
|
const newQuota = await provider.getQuota(meta.owner, meta.accountId, information.storage.quotaInBytes);
|
|
1701
1880
|
res.status(200).json({ status: 200, message: "Upload complete", data: { type: "UPLOAD_COMPLETE", driveId: String(drive._id), item }, statistic: { storage: newQuota } });
|
|
1702
1881
|
} catch (err) {
|
|
@@ -1725,10 +1904,10 @@ var driveAPIHandler = async (req, res) => {
|
|
|
1725
1904
|
const cancelData = cancelQuerySchema.safeParse(req.query);
|
|
1726
1905
|
if (!cancelData.success) return res.status(400).json({ status: 400, message: "Invalid ID" });
|
|
1727
1906
|
const { id } = cancelData.data;
|
|
1728
|
-
const tempUploadDir =
|
|
1729
|
-
if (
|
|
1907
|
+
const tempUploadDir = path.join(os2.tmpdir(), "next-drive-uploads", id);
|
|
1908
|
+
if (fs.existsSync(tempUploadDir)) {
|
|
1730
1909
|
try {
|
|
1731
|
-
|
|
1910
|
+
fs.rmSync(tempUploadDir, { recursive: true, force: true });
|
|
1732
1911
|
} catch (e) {
|
|
1733
1912
|
console.error("Failed to cleanup temp upload:", e);
|
|
1734
1913
|
}
|
|
@@ -1871,5 +2050,5 @@ var driveAPIHandler = async (req, res) => {
|
|
|
1871
2050
|
};
|
|
1872
2051
|
|
|
1873
2052
|
export { driveAPIHandler, driveConfiguration, driveDelete, driveFilePath, driveFileSchemaZod, driveGetUrl, driveInfo, driveList, driveReadFile, driveUpload, getDriveConfig, getDriveInformation };
|
|
1874
|
-
//# sourceMappingURL=chunk-
|
|
1875
|
-
//# sourceMappingURL=chunk-
|
|
2053
|
+
//# sourceMappingURL=chunk-MTGJTRD5.js.map
|
|
2054
|
+
//# sourceMappingURL=chunk-MTGJTRD5.js.map
|