@muhgholy/next-drive 4.23.30 → 4.23.32
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-J2OYAYYA.js → chunk-2DCZENJ2.js} +116 -10
- package/dist/chunk-2DCZENJ2.js.map +1 -0
- package/dist/{chunk-LTWWPQJA.cjs → chunk-HWNIRA6Y.cjs} +49 -6
- package/dist/chunk-HWNIRA6Y.cjs.map +1 -0
- package/dist/{chunk-DNJRZKAF.js → chunk-IWV6G7HQ.js} +49 -7
- package/dist/chunk-IWV6G7HQ.js.map +1 -0
- package/dist/{chunk-QE5EHVVA.cjs → chunk-WUG4ATLH.cjs} +150 -44
- package/dist/chunk-WUG4ATLH.cjs.map +1 -0
- package/dist/server/actions/public.d.ts.map +1 -1
- package/dist/server/config.d.ts.map +1 -1
- package/dist/server/express.cjs +12 -12
- package/dist/server/express.js +4 -4
- package/dist/server/hono.cjs +12 -12
- package/dist/server/hono.js +4 -4
- package/dist/server/index.cjs +19 -19
- package/dist/server/index.d.ts.map +1 -1
- package/dist/server/index.js +2 -2
- package/dist/server/storage-adapters/google.d.ts.map +1 -1
- package/dist/server/tus.d.ts +5 -1
- package/dist/server/tus.d.ts.map +1 -1
- package/dist/tus-5LLFMVQK.cjs +20 -0
- package/dist/tus-5LLFMVQK.cjs.map +1 -0
- package/dist/tus-C4T5ZDTG.js +3 -0
- package/dist/tus-C4T5ZDTG.js.map +1 -0
- package/dist/types/server/config.d.ts +8 -0
- package/dist/types/server/config.d.ts.map +1 -1
- package/dist/types/server/express.d.ts +3 -24
- package/dist/types/server/express.d.ts.map +1 -1
- package/dist/types/server/hono.d.ts +3 -24
- package/dist/types/server/hono.d.ts.map +1 -1
- package/package.json +1 -1
- package/dist/chunk-DNJRZKAF.js.map +0 -1
- package/dist/chunk-J2OYAYYA.js.map +0 -1
- package/dist/chunk-LTWWPQJA.cjs.map +0 -1
- package/dist/chunk-QE5EHVVA.cjs.map +0 -1
- package/dist/tus-35IUZMFV.js +0 -3
- package/dist/tus-35IUZMFV.js.map +0 -1
- package/dist/tus-PK6UHGE4.cjs +0 -16
- package/dist/tus-PK6UHGE4.cjs.map +0 -1
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { getDriveConfig, getDriveInformation, resolveProvider, drive_default, GoogleDriveProvider, LocalStorageProvider, getImageSettings, account_default, withSignedUrl, withSignedUrls, handleUpload } from './chunk-
|
|
1
|
+
import { getDriveConfig, getDriveInformation, resolveProvider, drive_default, GoogleDriveProvider, LocalStorageProvider, getImageSettings, account_default, withSignedUrl, withSignedUrls, handleUpload } from './chunk-IWV6G7HQ.js';
|
|
2
2
|
import path from 'path';
|
|
3
3
|
import fs from 'fs';
|
|
4
4
|
import crypto from 'crypto';
|
|
@@ -100,21 +100,68 @@ var handlePublicAction = async (req, res, action, config) => {
|
|
|
100
100
|
return true;
|
|
101
101
|
}
|
|
102
102
|
}
|
|
103
|
+
if (config.security?.publicRateLimit) {
|
|
104
|
+
const { publicRateLimit } = config.security;
|
|
105
|
+
const abuse = globalThis.__nextDrive.abuse;
|
|
106
|
+
const now = Date.now();
|
|
107
|
+
if (now - abuse.lastSweep > 6e4) {
|
|
108
|
+
abuse.lastSweep = now;
|
|
109
|
+
for (const [seenKey, seenHits] of abuse.ipHits) {
|
|
110
|
+
const fresh = seenHits.filter((t) => now - t < 36e5);
|
|
111
|
+
if (fresh.length) abuse.ipHits.set(seenKey, fresh);
|
|
112
|
+
else abuse.ipHits.delete(seenKey);
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
const ip = publicRateLimit.clientId?.(req) ?? (publicRateLimit.trustedHeaders ?? ["cf-connecting-ip", "x-real-ip", "x-forwarded-for"]).map((h) => req.headers[h]).find(Boolean)?.toString().split(",")[0].trim() ?? req.socket?.remoteAddress ?? "unknown";
|
|
116
|
+
const key = `serve:${ip}`;
|
|
117
|
+
const hits = (abuse.ipHits.get(key) ?? []).filter((t) => now - t < publicRateLimit.windowMinutes * 6e4);
|
|
118
|
+
if (hits.length >= publicRateLimit.max) {
|
|
119
|
+
res.status(429).json({ status: 429, message: "Too many requests, please try again later" });
|
|
120
|
+
return true;
|
|
121
|
+
}
|
|
122
|
+
hits.push(now);
|
|
123
|
+
abuse.ipHits.set(key, hits);
|
|
124
|
+
}
|
|
103
125
|
const itemProvider = drive.provider?.type === "GOOGLE" ? GoogleDriveProvider : LocalStorageProvider;
|
|
104
126
|
const itemAccountId = drive.storageAccountId ? drive.storageAccountId.toString() : void 0;
|
|
127
|
+
const fileHash = drive.information.type === "FILE" ? drive.information.hash : void 0;
|
|
128
|
+
const fileVersion = fileHash || drive._id.toString();
|
|
129
|
+
const ifNoneMatch = req.headers["if-none-match"];
|
|
105
130
|
if (action === "thumbnail") {
|
|
106
|
-
const
|
|
131
|
+
const etag2 = `"${fileVersion}-thumb"`;
|
|
107
132
|
res.setHeader("Content-Type", "image/webp");
|
|
133
|
+
res.setHeader("Cache-Control", "public, max-age=31536000, immutable");
|
|
134
|
+
res.setHeader("ETag", etag2);
|
|
108
135
|
if (config.cors?.enabled) {
|
|
109
136
|
res.setHeader("Cross-Origin-Resource-Policy", "cross-origin");
|
|
110
137
|
}
|
|
138
|
+
if (ifNoneMatch === etag2) {
|
|
139
|
+
res.status(304).end();
|
|
140
|
+
return true;
|
|
141
|
+
}
|
|
142
|
+
const abuse = globalThis.__nextDrive.abuse;
|
|
143
|
+
if (config.security?.transformConcurrency && abuse.transformActive >= config.security.transformConcurrency) {
|
|
144
|
+
res.status(503).json({ status: 503, message: "Server is busy processing images, please try again later" });
|
|
145
|
+
return true;
|
|
146
|
+
}
|
|
147
|
+
abuse.transformActive++;
|
|
148
|
+
let stream2;
|
|
149
|
+
try {
|
|
150
|
+
stream2 = await itemProvider.getThumbnail(drive, itemAccountId);
|
|
151
|
+
} finally {
|
|
152
|
+
abuse.transformActive--;
|
|
153
|
+
}
|
|
154
|
+
req.on("close", () => {
|
|
155
|
+
if (!res.writableEnded) stream2.destroy();
|
|
156
|
+
});
|
|
111
157
|
stream2.pipe(res);
|
|
112
158
|
return true;
|
|
113
159
|
}
|
|
114
160
|
const { stream, mime, size: fileSize } = await itemProvider.openStream(drive, itemAccountId);
|
|
115
161
|
const safeFilename = sanitizeContentDispositionFilename(drive.name);
|
|
116
162
|
const format = req.query.format;
|
|
117
|
-
const
|
|
163
|
+
const rawQuality = req.query.quality;
|
|
164
|
+
const quality = rawQuality && !isNaN(Number(rawQuality)) ? String(Math.round(Number(rawQuality) / 10) * 10) : rawQuality;
|
|
118
165
|
const display = req.query.display;
|
|
119
166
|
const sizePreset = req.query.size;
|
|
120
167
|
const fit = req.query.fit;
|
|
@@ -144,21 +191,49 @@ var handlePublicAction = async (req, res, action, config) => {
|
|
|
144
191
|
targetFormat
|
|
145
192
|
].join("_");
|
|
146
193
|
const cachePath = path.join(cacheDir, `${cacheKey}.bin`);
|
|
194
|
+
const etag2 = `"${fileVersion}-${cacheKey}"`;
|
|
195
|
+
res.setHeader("Cache-Control", "public, max-age=31536000, immutable");
|
|
196
|
+
res.setHeader("ETag", etag2);
|
|
197
|
+
if (ifNoneMatch === etag2) {
|
|
198
|
+
if ("destroy" in stream) {
|
|
199
|
+
stream.destroy();
|
|
200
|
+
}
|
|
201
|
+
res.status(304).end();
|
|
202
|
+
return true;
|
|
203
|
+
}
|
|
147
204
|
if (fs.existsSync(cachePath)) {
|
|
148
205
|
const cacheStat = fs.statSync(cachePath);
|
|
149
206
|
res.setHeader("Content-Type", `image/${targetFormat}`);
|
|
150
207
|
res.setHeader("Content-Length", cacheStat.size);
|
|
151
|
-
res.setHeader("Cache-Control", "public, max-age=31536000, immutable");
|
|
152
208
|
if (config.cors?.enabled) {
|
|
153
209
|
res.setHeader("Cross-Origin-Resource-Policy", "cross-origin");
|
|
154
210
|
}
|
|
155
211
|
if ("destroy" in stream) {
|
|
156
212
|
stream.destroy();
|
|
157
213
|
}
|
|
158
|
-
fs.createReadStream(cachePath)
|
|
214
|
+
const cachedStream = fs.createReadStream(cachePath);
|
|
215
|
+
req.on("close", () => {
|
|
216
|
+
if (!res.writableEnded) cachedStream.destroy();
|
|
217
|
+
});
|
|
218
|
+
cachedStream.pipe(res);
|
|
219
|
+
return true;
|
|
220
|
+
}
|
|
221
|
+
const abuse = globalThis.__nextDrive.abuse;
|
|
222
|
+
if (config.security?.transformConcurrency && abuse.transformActive >= config.security.transformConcurrency) {
|
|
223
|
+
if ("destroy" in stream) {
|
|
224
|
+
stream.destroy();
|
|
225
|
+
}
|
|
226
|
+
res.status(503).json({ status: 503, message: "Server is busy processing images, please try again later" });
|
|
159
227
|
return true;
|
|
160
228
|
}
|
|
161
229
|
if (!fs.existsSync(cacheDir)) fs.mkdirSync(cacheDir, { recursive: true });
|
|
230
|
+
if (config.security?.cacheMaxAgeDays) {
|
|
231
|
+
const maxAgeMs = config.security.cacheMaxAgeDays * 24 * 60 * 60 * 1e3;
|
|
232
|
+
for (const entry of fs.readdirSync(cacheDir)) {
|
|
233
|
+
const entryPath = path.join(cacheDir, entry);
|
|
234
|
+
if (Date.now() - fs.statSync(entryPath).mtimeMs > maxAgeMs) fs.unlinkSync(entryPath);
|
|
235
|
+
}
|
|
236
|
+
}
|
|
162
237
|
let pipeline = sharp();
|
|
163
238
|
if (settings.width && settings.height) {
|
|
164
239
|
pipeline = pipeline.resize(settings.width, settings.height, {
|
|
@@ -182,20 +257,43 @@ var handlePublicAction = async (req, res, action, config) => {
|
|
|
182
257
|
pipeline = pipeline.avif({ quality: settings.quality, effort: settings.effort });
|
|
183
258
|
res.setHeader("Content-Type", "image/avif");
|
|
184
259
|
}
|
|
185
|
-
|
|
260
|
+
abuse.transformActive++;
|
|
186
261
|
pipeline.on("error", (err) => {
|
|
262
|
+
abuse.transformActive--;
|
|
187
263
|
console.error("[next-drive] Pipeline error:", err);
|
|
188
264
|
});
|
|
265
|
+
pipeline.on("close", () => {
|
|
266
|
+
abuse.transformActive--;
|
|
267
|
+
});
|
|
189
268
|
stream.pipe(pipeline);
|
|
269
|
+
const outStream = pipeline.clone().pipe(res);
|
|
270
|
+
req.on("close", () => {
|
|
271
|
+
if (!res.writableEnded) outStream.destroy();
|
|
272
|
+
});
|
|
190
273
|
pipeline.clone().toFile(cachePath).catch((e) => console.error("[next-drive] Cache write failed:", e));
|
|
191
|
-
pipeline.clone().pipe(res);
|
|
192
274
|
return true;
|
|
193
275
|
} catch (e) {
|
|
194
276
|
console.error("[next-drive] Image transformation failed:", e);
|
|
195
277
|
}
|
|
196
278
|
}
|
|
279
|
+
const etag = `"${fileVersion}"`;
|
|
197
280
|
res.setHeader("Content-Type", mime);
|
|
198
281
|
if (fileSize) res.setHeader("Content-Length", fileSize);
|
|
282
|
+
res.setHeader("Cache-Control", "public, max-age=31536000, immutable");
|
|
283
|
+
res.setHeader("ETag", etag);
|
|
284
|
+
if (ifNoneMatch === etag) {
|
|
285
|
+
if ("destroy" in stream) {
|
|
286
|
+
stream.destroy();
|
|
287
|
+
}
|
|
288
|
+
res.status(304).end();
|
|
289
|
+
return true;
|
|
290
|
+
}
|
|
291
|
+
if ("destroy" in stream) {
|
|
292
|
+
const destroyable = stream;
|
|
293
|
+
req.on("close", () => {
|
|
294
|
+
if (!res.writableEnded) destroyable.destroy();
|
|
295
|
+
});
|
|
296
|
+
}
|
|
199
297
|
stream.pipe(res);
|
|
200
298
|
return true;
|
|
201
299
|
} catch (error) {
|
|
@@ -650,7 +748,15 @@ var parseJsonBody = (req) => {
|
|
|
650
748
|
return new Promise((resolve) => {
|
|
651
749
|
try {
|
|
652
750
|
const chunks = [];
|
|
653
|
-
|
|
751
|
+
let totalBytes = 0;
|
|
752
|
+
req.on("data", (chunk) => {
|
|
753
|
+
totalBytes += chunk.length;
|
|
754
|
+
if (totalBytes > 1024 * 1024) {
|
|
755
|
+
req.destroy();
|
|
756
|
+
return resolve({});
|
|
757
|
+
}
|
|
758
|
+
chunks.push(chunk);
|
|
759
|
+
});
|
|
654
760
|
req.on("end", () => {
|
|
655
761
|
const raw = Buffer.concat(chunks).toString();
|
|
656
762
|
if (!raw) return resolve({});
|
|
@@ -752,5 +858,5 @@ var driveAPIHandler = async (req, res) => {
|
|
|
752
858
|
};
|
|
753
859
|
|
|
754
860
|
export { driveAPIHandler, driveFileSchemaZod };
|
|
755
|
-
//# sourceMappingURL=chunk-
|
|
756
|
-
//# sourceMappingURL=chunk-
|
|
861
|
+
//# sourceMappingURL=chunk-2DCZENJ2.js.map
|
|
862
|
+
//# sourceMappingURL=chunk-2DCZENJ2.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/server/actions/cors.ts","../src/server/security/crypto-utils.ts","../src/server/actions/public.ts","../src/server/actions/auth.ts","../src/server/zod/schemas.ts","../src/server/actions/drive.ts","../src/server/index.ts"],"names":["etag","stream","authenticated"],"mappings":"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","file":"chunk-2DCZENJ2.js","sourcesContent":["import type { NextApiRequest, NextApiResponse } from 'next';\nimport { getDriveConfig } from '@/server/config';\n\nexport const applyCorsHeaders = (req: NextApiRequest, res: NextApiResponse, config: ReturnType<typeof getDriveConfig>): boolean => {\n const cors = config.cors;\n if (!cors?.enabled) return false;\n\n const origin = req.headers.origin;\n const allowedOrigins = cors.origins ?? '*';\n const methods = cors.methods ?? ['GET', 'POST', 'PUT', 'PATCH', 'DELETE', 'HEAD', 'OPTIONS'];\n const allowedHeaders = cors.allowedHeaders ?? ['Content-Type', 'Authorization', 'X-Drive-Account', 'Tus-Resumable', 'Upload-Length', 'Upload-Offset', 'Upload-Metadata', 'Upload-Concat', 'Upload-Defer-Length'];\n const exposedHeaders = cors.exposedHeaders ?? ['Content-Length', 'Content-Type', 'Content-Disposition', 'Location', 'Tus-Resumable', 'Upload-Length', 'Upload-Offset', 'Upload-Metadata', 'Upload-Expires', 'X-Drive-Item'];\n const credentials = cors.credentials ?? false;\n const maxAge = cors.maxAge ?? 86400;\n\n let allowOrigin: string | null = null;\n if (origin) {\n if (allowedOrigins === '*') {\n allowOrigin = origin;\n } else if (Array.isArray(allowedOrigins)) {\n if (allowedOrigins.includes(origin)) {\n allowOrigin = origin;\n }\n } else if (allowedOrigins === origin) {\n allowOrigin = origin;\n }\n } else if (allowedOrigins === '*') {\n allowOrigin = '*';\n }\n\n if (!allowOrigin) {\n if (req.method === 'OPTIONS') {\n res.status(403).end();\n return true;\n }\n return false;\n }\n\n res.setHeader('Access-Control-Allow-Origin', allowOrigin);\n res.setHeader('Access-Control-Allow-Methods', methods.join(', '));\n res.setHeader('Access-Control-Allow-Headers', allowedHeaders.join(', '));\n res.setHeader('Access-Control-Expose-Headers', exposedHeaders.join(', '));\n res.setHeader('Access-Control-Max-Age', maxAge.toString());\n\n if (credentials) {\n res.setHeader('Access-Control-Allow-Credentials', 'true');\n }\n\n if (req.method === 'OPTIONS') {\n res.status(204).end();\n return true;\n }\n\n return false;\n};\n","import crypto from 'crypto';\r\n\r\n/**\r\n * Constant-time string comparison to prevent timing attacks\r\n */\r\nexport function constantTimeCompare(a: string, b: string): boolean {\r\n\tif (a.length !== b.length) {\r\n\t\treturn false;\r\n\t}\r\n\r\n\tlet result = 0;\r\n\tfor (let i = 0; i < a.length; i++) {\r\n\t\tresult |= a.charCodeAt(i) ^ b.charCodeAt(i);\r\n\t}\r\n\r\n\treturn result === 0;\r\n}\r\n\r\n/**\r\n * Safe error message that doesn't leak sensitive information\r\n */\r\nexport function getSafeErrorMessage(error: unknown): string {\r\n\tif (error instanceof Error) {\r\n\t\t// Filter out sensitive patterns (case-insensitive)\r\n\t\tconst message = error.message.toLowerCase();\r\n\r\n\t\t// Don't expose database errors\r\n\t\tif (message.includes('mongo')) {\r\n\t\t\treturn 'Database operation failed';\r\n\t\t}\r\n\r\n\t\t// Don't expose file paths\r\n\t\tif (message.includes('/') || message.includes('\\\\')) {\r\n\t\t\treturn 'File operation failed';\r\n\t\t}\r\n\r\n\t\t// Don't expose validation details from internal libraries\r\n\t\tif (message.includes('validation') || message.includes('cast')) {\r\n\t\t\treturn 'Invalid input';\r\n\t\t}\r\n\r\n\t\t// Generic safe messages\r\n\t\treturn 'Operation failed';\r\n\t}\r\n\r\n\treturn 'Internal server error';\r\n}\r\n\r\n/**\r\n * Validate and sanitize Content-Disposition filename\r\n */\r\nexport function sanitizeContentDispositionFilename(filename: string): string {\r\n\t// Remove any path components\r\n\tconst basename = filename.replace(/^.*[\\\\\\/]/, '');\r\n\r\n\t// Remove or encode dangerous characters\r\n\treturn basename\r\n\t\t.replace(/[\"\\r\\n]/g, '')\r\n\t\t.replace(/[^\\x20-\\x7E]/g, '') // Remove non-printable ASCII\r\n\t\t.slice(0, 255);\r\n}\r\n","import type { NextApiRequest, NextApiResponse } from 'next';\nimport path from 'path';\nimport fs from 'fs';\nimport crypto from 'crypto';\nimport sharp from 'sharp';\nimport { getDriveConfig } from '@/server/config';\nimport Drive from '@/server/database/mongoose/schema/drive';\nimport { getImageSettings } from '@/server/utils';\nimport { sanitizeContentDispositionFilename } from '@/server/security/crypto-utils';\nimport { LocalStorageProvider } from '@/server/storage-adapters/local';\nimport { GoogleDriveProvider } from '@/server/storage-adapters/google';\n\nexport const handlePublicAction = async (\n req: NextApiRequest,\n res: NextApiResponse,\n action: string,\n config: ReturnType<typeof getDriveConfig>\n): Promise<boolean> => {\n if (action !== 'serve' && action !== 'thumbnail') {\n return false;\n }\n\n try {\n const { id, token } = req.query;\n if (!id || typeof id !== 'string') {\n res.status(400).json({ status: 400, message: 'Could not open file: missing or invalid file ID' });\n return true;\n }\n\n const drive = await Drive.findById(id);\n if (!drive) {\n res.status(404).json({ status: 404, message: 'File not found or no longer available' });\n return true;\n }\n\n if (config.security?.signedUrls?.enabled) {\n if (!token || typeof token !== 'string') {\n res.status(401).json({ status: 401, message: 'Access denied: this link is missing its access token' });\n return true;\n }\n\n try {\n const decoded = Buffer.from(token, 'base64url').toString();\n const [expiryStr, signature] = decoded.split(':');\n const expiry = parseInt(expiryStr, 10);\n\n if (Date.now() / 1000 > expiry) {\n res.status(401).json({ status: 401, message: 'Access denied: this link has expired' });\n return true;\n }\n\n const { secret } = config.security.signedUrls;\n const expectedSignature = crypto.createHmac('sha256', secret)\n .update(`${id}:${expiry}`)\n .digest('hex');\n\n if (signature !== expectedSignature) {\n res.status(401).json({ status: 401, message: 'Access denied: this link\\'s access token is invalid' });\n return true;\n }\n } catch {\n res.status(401).json({ status: 401, message: 'Access denied: this link\\'s access token is malformed' });\n return true;\n }\n }\n\n // ** Per-IP rate limit for serve/thumbnail (opt-in via config.security.publicRateLimit). Reuses\n // ** the same `ipHits` map the anonymous-upload abuse tracker uses (src/server/tus.ts), with a\n // ** `serve:` key prefix so the two counters never mix.\n if (config.security?.publicRateLimit) {\n const { publicRateLimit } = config.security;\n const abuse = (globalThis as any).__nextDrive.abuse;\n const now = Date.now();\n\n // ** Throttled global sweep (shares `lastSweep`/the pruning logic with tus.ts's own sweep)\n // ** so `ipHits` cannot grow unbounded when publicRateLimit is used without anonymous uploads.\n if (now - abuse.lastSweep > 60000) {\n abuse.lastSweep = now;\n for (const [seenKey, seenHits] of abuse.ipHits) {\n const fresh = (seenHits as number[]).filter(t => now - t < 3600000);\n if (fresh.length) abuse.ipHits.set(seenKey, fresh);\n else abuse.ipHits.delete(seenKey);\n }\n }\n\n // ** Same resolution order/override pattern as `unauthenticated.abuse` (src/server/tus.ts):\n // ** `clientId` full override, then the first trusted header present (handling both string\n // ** and string[] header values), then the raw socket - all safely optional-chained so a\n // ** missing header or a proxy/adapter without a socket (Docker, non-Node adapters) never\n // ** throws and always falls back to a stable, if imprecise, identifier.\n const ip =\n publicRateLimit.clientId?.(req) ??\n (publicRateLimit.trustedHeaders ?? ['cf-connecting-ip', 'x-real-ip', 'x-forwarded-for'])\n .map(h => req.headers[h])\n .find(Boolean)\n ?.toString().split(',')[0].trim() ??\n req.socket?.remoteAddress ??\n 'unknown';\n const key = `serve:${ip}`;\n const hits = (abuse.ipHits.get(key) ?? []).filter((t: number) => now - t < publicRateLimit.windowMinutes * 60000);\n if (hits.length >= publicRateLimit.max) {\n res.status(429).json({ status: 429, message: 'Too many requests, please try again later' });\n return true;\n }\n hits.push(now);\n abuse.ipHits.set(key, hits);\n }\n\n const itemProvider = drive.provider?.type === 'GOOGLE' ? GoogleDriveProvider : LocalStorageProvider;\n const itemAccountId = drive.storageAccountId ? drive.storageAccountId.toString() : undefined;\n const fileHash = drive.information.type === 'FILE' ? drive.information.hash : undefined;\n const fileVersion = fileHash || drive._id.toString();\n const ifNoneMatch = req.headers['if-none-match'];\n\n if (action === 'thumbnail') {\n const etag = `\"${fileVersion}-thumb\"`;\n res.setHeader('Content-Type', 'image/webp');\n res.setHeader('Cache-Control', 'public, max-age=31536000, immutable');\n res.setHeader('ETag', etag);\n if (config.cors?.enabled) {\n res.setHeader('Cross-Origin-Resource-Policy', 'cross-origin');\n }\n if (ifNoneMatch === etag) {\n res.status(304).end();\n return true;\n }\n // ** Same transform-concurrency guard as the resize/re-encode path below - thumbnail\n // ** generation also runs sharp/ffmpeg on a cache miss, so it needs the same cap.\n const abuse = (globalThis as any).__nextDrive.abuse;\n if (config.security?.transformConcurrency && abuse.transformActive >= config.security.transformConcurrency) {\n res.status(503).json({ status: 503, message: 'Server is busy processing images, please try again later' });\n return true;\n }\n abuse.transformActive++;\n let stream;\n try {\n stream = await itemProvider.getThumbnail(drive, itemAccountId);\n } finally {\n abuse.transformActive--;\n }\n req.on('close', () => { if (!res.writableEnded) stream.destroy(); });\n stream.pipe(res);\n return true;\n }\n\n const { stream, mime, size: fileSize } = await itemProvider.openStream(drive, itemAccountId);\n const safeFilename = sanitizeContentDispositionFilename(drive.name);\n\n const format = req.query.format as string;\n // ** Bucket numeric quality to the nearest 10 so an attacker varying it 1..100 can't force a\n // ** unique cache entry / fresh sharp re-encode on every request (cache-key explosion).\n const rawQuality = req.query.quality as string;\n const quality = rawQuality && !isNaN(Number(rawQuality)) ? String(Math.round(Number(rawQuality) / 10) * 10) : rawQuality;\n const display = req.query.display as string;\n const sizePreset = req.query.size as string;\n const fit = req.query.fit as string;\n const position = req.query.position as string;\n\n const isImage = mime.startsWith('image/');\n const shouldTransform = isImage && (format || quality || display || sizePreset || fit);\n\n res.setHeader('Content-Disposition', `inline; filename=\"${safeFilename}\"`);\n\n if (config.cors?.enabled) {\n res.setHeader('Cross-Origin-Resource-Policy', 'cross-origin');\n }\n\n if (shouldTransform) {\n try {\n const settings = getImageSettings(fileSize, quality, display, sizePreset, fit, position);\n\n let targetFormat = format || mime.split('/')[1];\n if (targetFormat === 'jpg') targetFormat = 'jpeg';\n if (!['jpeg', 'png', 'webp', 'avif'].includes(targetFormat)) {\n targetFormat = format || 'webp';\n }\n\n const cacheDir = path.join(config.storage.path, 'file', drive._id.toString(), 'cache');\n const cacheKey = [\n 'opt',\n `q${settings.quality}`,\n `e${settings.effort}`,\n settings.width ? `${settings.width}x${settings.height}` : 'orig',\n settings.fit || 'none',\n settings.position || 'c',\n targetFormat,\n ].join('_');\n const cachePath = path.join(cacheDir, `${cacheKey}.bin`);\n const etag = `\"${fileVersion}-${cacheKey}\"`;\n\n res.setHeader('Cache-Control', 'public, max-age=31536000, immutable');\n res.setHeader('ETag', etag);\n\n if (ifNoneMatch === etag) {\n if ('destroy' in stream) {\n (stream as NodeJS.ReadableStream & { destroy: () => void }).destroy();\n }\n res.status(304).end();\n return true;\n }\n\n if (fs.existsSync(cachePath)) {\n const cacheStat = fs.statSync(cachePath);\n res.setHeader('Content-Type', `image/${targetFormat}`);\n res.setHeader('Content-Length', cacheStat.size);\n if (config.cors?.enabled) {\n res.setHeader('Cross-Origin-Resource-Policy', 'cross-origin');\n }\n if ('destroy' in stream) {\n (stream as NodeJS.ReadableStream & { destroy: () => void }).destroy();\n }\n const cachedStream = fs.createReadStream(cachePath);\n req.on('close', () => { if (!res.writableEnded) cachedStream.destroy(); });\n cachedStream.pipe(res);\n return true;\n }\n\n // ** Cap simultaneous sharp pipelines (opt-in via config.security.transformConcurrency) so a\n // ** burst of requests for uncached variants can't spin up unbounded CPU-heavy transforms.\n const abuse = (globalThis as any).__nextDrive.abuse;\n if (config.security?.transformConcurrency && abuse.transformActive >= config.security.transformConcurrency) {\n if ('destroy' in stream) {\n (stream as NodeJS.ReadableStream & { destroy: () => void }).destroy();\n }\n res.status(503).json({ status: 503, message: 'Server is busy processing images, please try again later' });\n return true;\n }\n\n if (!fs.existsSync(cacheDir)) fs.mkdirSync(cacheDir, { recursive: true });\n\n // ** Evict stale variants from this file's own cache folder on a miss (opt-in via\n // ** config.security.cacheMaxAgeDays) so disk usage doesn't grow forever with every\n // ** quality/format/size combination ever requested. Folder only holds this one file's\n // ** variants (typically a handful), so a full scan here is cheap.\n if (config.security?.cacheMaxAgeDays) {\n const maxAgeMs = config.security.cacheMaxAgeDays * 24 * 60 * 60 * 1000;\n for (const entry of fs.readdirSync(cacheDir)) {\n const entryPath = path.join(cacheDir, entry);\n if (Date.now() - fs.statSync(entryPath).mtimeMs > maxAgeMs) fs.unlinkSync(entryPath);\n }\n }\n\n let pipeline = sharp();\n\n if (settings.width && settings.height) {\n pipeline = pipeline.resize(settings.width, settings.height, {\n fit: settings.fit || 'inside',\n position: settings.position || 'center',\n withoutEnlargement: true,\n background: { r: 0, g: 0, b: 0, alpha: 0 },\n });\n }\n\n if (targetFormat === 'jpeg') {\n pipeline = pipeline.jpeg({ quality: settings.quality, mozjpeg: true });\n res.setHeader('Content-Type', 'image/jpeg');\n } else if (targetFormat === 'png') {\n pipeline = pipeline.png({ compressionLevel: settings.pngCompression, adaptiveFiltering: true });\n res.setHeader('Content-Type', 'image/png');\n } else if (targetFormat === 'webp') {\n const webpEffort = Math.min(settings.effort, 6);\n pipeline = pipeline.webp({ quality: settings.quality, effort: webpEffort });\n res.setHeader('Content-Type', 'image/webp');\n } else if (targetFormat === 'avif') {\n pipeline = pipeline.avif({ quality: settings.quality, effort: settings.effort });\n res.setHeader('Content-Type', 'image/avif');\n }\n\n abuse.transformActive++;\n pipeline.on('error', (err) => {\n abuse.transformActive--;\n console.error('[next-drive] Pipeline error:', err);\n });\n pipeline.on('close', () => { abuse.transformActive--; });\n\n stream.pipe(pipeline);\n\n const outStream = pipeline.clone().pipe(res);\n req.on('close', () => { if (!res.writableEnded) outStream.destroy(); });\n pipeline.clone().toFile(cachePath).catch(e => console.error('[next-drive] Cache write failed:', e));\n return true;\n } catch (e) {\n console.error('[next-drive] Image transformation failed:', e);\n }\n }\n\n const etag = `\"${fileVersion}\"`;\n res.setHeader('Content-Type', mime);\n if (fileSize) res.setHeader('Content-Length', fileSize);\n res.setHeader('Cache-Control', 'public, max-age=31536000, immutable');\n res.setHeader('ETag', etag);\n if (ifNoneMatch === etag) {\n if ('destroy' in stream) {\n (stream as NodeJS.ReadableStream & { destroy: () => void }).destroy();\n }\n res.status(304).end();\n return true;\n }\n if ('destroy' in stream) {\n const destroyable = stream as NodeJS.ReadableStream & { destroy: () => void };\n req.on('close', () => { if (!res.writableEnded) destroyable.destroy(); });\n }\n stream.pipe(res);\n return true;\n } catch (error) {\n console.error(`[next-drive] Error in ${action}:`, error);\n const detail = error instanceof Error ? error.message : 'Something went wrong while serving the file';\n res.status(500).json({ status: 500, message: `Request \"${action}\" failed: ${detail}` });\n return true;\n }\n};\n","import type { NextApiRequest, NextApiResponse } from 'next';\nimport { google } from 'googleapis';\nimport { getDriveConfig } from '@/server/config';\nimport StorageAccount from '@/server/database/mongoose/schema/storage/account';\nimport Drive from '@/server/database/mongoose/schema/drive';\nimport { GoogleDriveProvider } from '@/server/storage-adapters/google';\n\nexport const handleAuthAction = async (\n req: NextApiRequest,\n res: NextApiResponse,\n action: string,\n config: ReturnType<typeof getDriveConfig>,\n owner: Record<string, unknown> | null\n): Promise<boolean> => {\n if (!['getAuthUrl', 'callback', 'listAccounts', 'removeAccount'].includes(action)) {\n return false;\n }\n\n switch (action) {\n case 'getAuthUrl': {\n const { provider } = req.query;\n if (provider === 'GOOGLE') {\n const { clientId, clientSecret, redirectUri } = config.storage?.google || {};\n if (!clientId || !clientSecret || !redirectUri) {\n res.status(500).json({ status: 500, message: 'Google Drive is not configured on the server' });\n return true;\n }\n\n const callbackUri = new URL(redirectUri);\n callbackUri.searchParams.set('action', 'callback');\n const oAuth2Client = new google.auth.OAuth2(clientId, clientSecret, callbackUri.toString());\n const state = Buffer.from(JSON.stringify({ owner })).toString('base64');\n const url = oAuth2Client.generateAuthUrl({\n access_type: 'offline',\n scope: ['https://www.googleapis.com/auth/drive', 'https://www.googleapis.com/auth/userinfo.email'],\n state,\n prompt: 'consent',\n });\n res.status(200).json({ status: 200, message: 'Auth URL generated', data: { url } });\n return true;\n }\n res.status(400).json({ status: 400, message: 'Unknown storage provider requested' });\n return true;\n }\n\n case 'callback': {\n const { code } = req.query;\n if (!code) {\n res.status(400).json({ status: 400, message: 'Google sign-in failed: authorization code missing' });\n return true;\n }\n\n const { clientId, clientSecret, redirectUri } = config.storage?.google || {};\n if (!clientId || !clientSecret || !redirectUri) {\n res.status(500).json({ status: 500, message: 'Google Drive sign-in is not configured on the server' });\n return true;\n }\n\n const callbackUri = new URL(redirectUri);\n callbackUri.searchParams.set('action', 'callback');\n const oAuth2Client = new google.auth.OAuth2(clientId, clientSecret, callbackUri.toString());\n\n const { tokens } = await oAuth2Client.getToken(code as string);\n oAuth2Client.setCredentials(tokens);\n\n const oauth2 = google.oauth2({ version: 'v2', auth: oAuth2Client });\n const userInfo = await oauth2.userinfo.get();\n\n const existing = await StorageAccount.findOne({ owner, 'metadata.google.email': userInfo.data.email, 'metadata.provider': 'GOOGLE' });\n if (existing) {\n existing.metadata.google.credentials = tokens as Record<string, unknown>;\n existing.markModified('metadata');\n await existing.save();\n } else {\n const newAccount = new StorageAccount({\n owner,\n name: userInfo.data.name || 'Google Drive',\n metadata: {\n provider: 'GOOGLE',\n google: {\n email: userInfo.data.email,\n credentials: tokens as Record<string, unknown>,\n },\n },\n });\n await newAccount.save();\n }\n\n res.setHeader('Content-Type', 'text/html');\n res.send(`<!DOCTYPE html>\n<html>\n<head><title>Authentication Complete</title></head>\n<body>\n<p>Authentication successful! This window will close automatically.</p>\n<script>\n(function() {\n\\tif (window.opener) {\n\\t\\ttry {\n\\t\\t\\twindow.opener.postMessage('oauth-success', '*');\n\\t\\t} catch (e) {}\n\\t}\n\\ttry {\n\\t\\tlocalStorage.setItem('next-drive-oauth-success', Date.now().toString());\n\\t\\tlocalStorage.removeItem('next-drive-oauth-success');\n\\t} catch (e) {}\n\\twindow.close();\n\\tsetTimeout(function() {\n\\t\\tdocument.body.innerHTML = '<p style=\"font-family: system-ui; text-align: center; margin-top: 50px;\">Authentication successful!<br>You can close this tab now.</p>';\n\\t}, 500);\n})();\n</script>\n</body>\n</html>`);\n return true;\n }\n\n case 'listAccounts': {\n const accounts = await StorageAccount.find({ owner });\n res.status(200).json({\n status: 200,\n data: {\n accounts: accounts.map(a => ({\n id: a._id.toString(),\n name: a.name,\n email: a.metadata.google?.email || '',\n provider: a.metadata.provider,\n })),\n },\n });\n return true;\n }\n\n case 'removeAccount': {\n const { id } = req.query;\n const account = await StorageAccount.findOne({ _id: id, owner });\n if (!account) {\n res.status(404).json({ status: 404, message: 'Could not disconnect: account not found' });\n return true;\n }\n\n if (account.metadata.provider === 'GOOGLE') {\n try {\n await GoogleDriveProvider.revokeToken(owner, account._id.toString());\n } catch (e) {\n console.error('Failed to revoke Google token:', e);\n }\n }\n\n await StorageAccount.deleteOne({ _id: id, owner });\n await Drive.deleteMany({ owner, storageAccountId: id });\n res.status(200).json({ status: 200, message: 'Account removed' });\n return true;\n }\n\n default:\n return false;\n }\n};\n","import { z } from 'zod';\r\nimport { isValidObjectId } from 'mongoose';\r\n\r\n// ** Custom ObjectId validator\r\nconst objectIdSchema = z.string().refine(val => isValidObjectId(val), {\r\n message: 'Invalid ObjectId format',\r\n});\r\n\r\n// ** Sanitize filename - remove path traversal and dangerous characters\r\nconst sanitizeFilename = (name: string): string => {\r\n return (\r\n name\r\n .replace(/[<>:\"|?*\\x00-\\x1F]/g, '') // Remove dangerous chars\r\n .replace(/^\\.+/, '') // Remove leading dots\r\n .replace(/\\.+$/, '') // Remove trailing dots\r\n .replace(/\\\\/g, '/') // Normalize slashes\r\n .replace(/\\/+/g, '/') // Remove duplicate slashes\r\n .replace(/\\.\\.\\//g, '') // Remove path traversal\r\n .replace(/\\.\\.+/g, '') // Remove remaining ..\r\n .split('/')\r\n .pop() ||\r\n '' // Take only the filename part (remove all paths)\r\n .trim()\r\n .slice(0, 255)\r\n ); // Limit length\r\n};\r\n\r\n// ** Sanitize search query for regex\r\nconst sanitizeRegexInput = (input: string): string => {\r\n // Escape regex special characters\r\n return input.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\$&').slice(0, 100);\r\n};\r\n\r\n// ** File/Folder name schema\r\nconst nameSchema = z\r\n .string()\r\n .min(1, 'Name is required')\r\n .max(255, 'Name too long')\r\n .transform(sanitizeFilename)\r\n .refine(val => val.length > 0, { message: 'Invalid name after sanitization' });\r\n\r\n// ** List query schema\r\nexport const listQuerySchema = z.object({\r\n folderId: z.union([z.literal('root'), objectIdSchema, z.undefined()]),\r\n limit: z\r\n .string()\r\n .optional()\r\n .transform(val => {\r\n const num = parseInt(val || '50', 10);\r\n return Math.min(Math.max(1, num), 100);\r\n }),\r\n afterId: objectIdSchema.optional(),\r\n});\r\n\r\n// ** Serve query schema\r\nexport const serveQuerySchema = z.object({\r\n id: objectIdSchema,\r\n token: z.string().optional(),\r\n});\r\n\r\n// ** Thumbnail query schema\r\nexport const thumbnailQuerySchema = z.object({\r\n id: objectIdSchema,\r\n size: z.enum(['small', 'medium', 'large']).optional().default('medium'),\r\n token: z.string().optional(),\r\n});\r\n\r\n// ** Rename body schema\r\nexport const renameBodySchema = z.object({\r\n id: objectIdSchema,\r\n newName: nameSchema,\r\n});\r\n\r\n// ** Delete query schema\r\nexport const deleteQuerySchema = z.object({\r\n id: objectIdSchema,\r\n});\r\n\r\n// ** Delete many body schema\r\nexport const deleteManyBodySchema = z.object({\r\n ids: z.array(objectIdSchema).min(1).max(1000),\r\n});\r\n\r\n// ** Create folder body schema\r\nexport const createFolderBodySchema = z.object({\r\n name: nameSchema,\r\n parentId: z.union([z.literal('root'), objectIdSchema, z.string().length(0), z.undefined()]).optional(),\r\n});\r\n\r\n// ** Move body schema\r\nexport const moveBodySchema = z.object({\r\n ids: z.array(objectIdSchema).min(1).max(1000),\r\n targetFolderId: z.union([z.literal('root'), objectIdSchema, z.undefined()]).optional(),\r\n});\r\n\r\n// ** Reorder body schema\r\nexport const reorderBodySchema = z.object({\r\n ids: z.array(objectIdSchema).min(1).max(1000),\r\n});\r\n\r\n// ** Search query schema\r\nexport const searchQuerySchema = z.object({\r\n q: z.string().min(1).max(100).transform(sanitizeRegexInput),\r\n folderId: z.union([z.literal('root'), objectIdSchema, z.undefined()]).optional(),\r\n limit: z\r\n .string()\r\n .optional()\r\n .transform(val => {\r\n const num = parseInt(val || '50', 10);\r\n return Math.min(Math.max(1, num), 100);\r\n }),\r\n trashed: z\r\n .string()\r\n .optional()\r\n .transform(val => val === 'true'),\r\n});\r\n\r\n// ** Restore query schema\r\nexport const restoreQuerySchema = z.object({\r\n id: objectIdSchema,\r\n});\r\n\r\n// ** Purge trash query schema\r\nexport const purgeTrashQuerySchema = z.object({\r\n days: z.number().int().min(1).max(365).optional(),\r\n});\r\n\r\n// ** Drive File Schema (Public)\r\nexport const driveFileSchemaZod = z.object({\r\n id: z.string(),\r\n isFolder: z.boolean(),\r\n file: z.object({\r\n name: z.string(),\r\n mime: z.string(),\r\n size: z.number(),\r\n }),\r\n});\r\n\r\n// ** Constants\r\nexport const MAX_FOLDER_DEPTH = 50;\r\n\r\n// ** Export sanitization functions\r\nexport { sanitizeFilename, sanitizeRegexInput };\r\n","import type { NextApiRequest, NextApiResponse } from 'next';\nimport type { IncomingMessage, ServerResponse } from 'http';\nimport { getDriveConfig } from '@/server/config';\nimport * as schemas from '@/server/zod/schemas';\nimport { handleUpload } from '@/server/tus';\nimport type { TDatabaseDrive, TDriveConfigInformation } from '@/types/server';\nimport type { TStorageProvider } from '@/types/server/storage';\nimport { GoogleDriveProvider } from '@/server/storage-adapters/google';\nimport { LocalStorageProvider } from '@/server/storage-adapters/local';\nimport { withSignedUrl, withSignedUrls, Drive } from '@/server/actions/shared';\nimport { resolveProvider } from '@/server/actions/shared';\n\ntype TDriveActionContext = {\n req: NextApiRequest;\n res: NextApiResponse;\n action: string;\n config: ReturnType<typeof getDriveConfig>;\n owner: Record<string, unknown> | null;\n isRootMode: boolean;\n authenticated: boolean;\n information: TDriveConfigInformation;\n provider: TStorageProvider;\n accountId?: string;\n};\n\nexport const handleDriveAction = async (ctx: TDriveActionContext): Promise<void> => {\n const { req, res, action, config, owner, isRootMode, authenticated, information, provider, accountId } = ctx;\n\n switch (action) {\n case 'list': {\n if (req.method !== 'GET') return void res.status(405).json({ status: 405, message: 'Listing files requires a GET request' });\n const listQuery = schemas.listQuerySchema.safeParse(req.query);\n if (!listQuery.success) return void res.status(400).json({ status: 400, message: 'Could not list files: invalid request parameters' });\n\n const { folderId, limit, afterId } = listQuery.data;\n\n try {\n await provider.sync(folderId || 'root', owner, accountId);\n } catch (e) {\n console.error('Sync failed', e);\n }\n\n const query: Record<string, unknown> = {\n 'provider.type': provider.name,\n storageAccountId: accountId || null,\n parentId: folderId === 'root' || !folderId ? null : folderId,\n trashedAt: null,\n };\n\n if (!isRootMode) {\n query.owner = owner;\n }\n\n if (afterId) query._id = { $lt: afterId };\n\n const items = await Drive.find(query, {}, { sort: { order: 1, _id: -1 }, limit });\n const plainItems = withSignedUrls(await Promise.all(items.map(item => item.toClient())), config);\n\n res.status(200).json({ status: 200, message: 'Items retrieved', data: { items: plainItems, hasMore: items.length === limit } });\n return;\n }\n\n case 'search': {\n const searchData = schemas.searchQuerySchema.safeParse(req.query);\n if (!searchData.success) return void res.status(400).json({ status: 400, message: 'Could not search: invalid request parameters' });\n const { q, folderId, limit, trashed } = searchData.data;\n\n if (!trashed) {\n try {\n await provider.search(q, owner, accountId);\n } catch (e) {\n console.error('Search sync failed', e);\n }\n }\n\n const query: Record<string, unknown> = {\n 'provider.type': provider.name,\n storageAccountId: accountId || null,\n trashedAt: trashed ? { $ne: null } : null,\n name: { $regex: q, $options: 'i' },\n };\n\n if (!isRootMode) {\n query.owner = owner;\n }\n\n if (folderId && folderId !== 'root') query.parentId = folderId;\n\n const items = await Drive.find(query, {}, { limit, sort: { createdAt: -1 } });\n const plainItems = withSignedUrls(await Promise.all(items.map(i => i.toClient())), config);\n\n res.status(200).json({ status: 200, message: 'Results', data: { items: plainItems } });\n return;\n }\n\n case 'upload': {\n // ** tus resumable upload protocol owns POST/HEAD/PATCH/DELETE for `?action=upload`.\n // ** Context is passed to the tus hooks (src/server/tus.ts) via the web Request.\n await handleUpload(req as unknown as IncomingMessage, res as unknown as ServerResponse, { config, owner, authenticated, isRootMode, information, provider, accountId });\n return;\n }\n\n case 'createFolder': {\n const folderData = schemas.createFolderBodySchema.safeParse(req.body);\n if (!folderData.success) return void res.status(400).json({ status: 400, message: folderData.error.errors[0].message });\n const { name, parentId } = folderData.data;\n\n const item = withSignedUrl(await provider.createFolder(name, parentId ?? null, owner, accountId), config);\n res.status(201).json({ status: 201, message: 'Folder created', data: { item } });\n return;\n }\n\n case 'delete': {\n const deleteData = schemas.deleteQuerySchema.safeParse(req.query);\n if (!deleteData.success) return void res.status(400).json({ status: 400, message: 'Could not move to trash: invalid ID' });\n const { id } = deleteData.data;\n const drive = await Drive.findById(id);\n if (!drive) return void res.status(404).json({ status: 404, message: 'Could not move to trash: item not found' });\n\n const itemProvider = drive.provider?.type === 'GOOGLE' ? GoogleDriveProvider : LocalStorageProvider;\n const itemAccountId = drive.storageAccountId ? drive.storageAccountId.toString() : undefined;\n\n try {\n await itemProvider.trash([id], owner, itemAccountId);\n } catch (e) {\n console.error('Provider trash failed:', e);\n }\n\n drive.trashedAt = new Date();\n await drive.save();\n res.status(200).json({ status: 200, message: 'Moved to trash', data: null });\n return;\n }\n\n case 'deletePermanent': {\n const deleteData = schemas.deleteQuerySchema.safeParse(req.query);\n if (!deleteData.success) return void res.status(400).json({ status: 400, message: 'Could not delete: invalid ID' });\n const { id } = deleteData.data;\n await provider.delete([id], owner, accountId);\n const quota = await provider.getQuota(owner, accountId, information.storage.quotaInBytes);\n res.status(200).json({ status: 200, message: 'Deleted', statistic: { storage: quota } });\n return;\n }\n\n case 'quota': {\n const quota = await provider.getQuota(owner, accountId, information.storage.quotaInBytes);\n res.status(200).json({\n status: 200,\n message: 'Quota retrieved',\n data: {\n usedInBytes: quota.usedInBytes,\n totalInBytes: quota.quotaInBytes,\n availableInBytes: Math.max(0, quota.quotaInBytes - quota.usedInBytes),\n percentage: quota.quotaInBytes > 0 ? Math.round((quota.usedInBytes / quota.quotaInBytes) * 100) : 0,\n },\n statistic: { storage: quota },\n });\n return;\n }\n\n case 'trash': {\n try {\n const { provider: trashProvider, accountId: trashAccountId } = await resolveProvider(req, owner);\n await trashProvider.syncTrash(owner, trashAccountId);\n } catch (e) {\n console.error('Trash sync failed', e);\n }\n\n const query: Record<string, unknown> = {\n owner,\n 'provider.type': provider.name,\n storageAccountId: accountId || null,\n trashedAt: { $ne: null },\n };\n\n const items = await Drive.find(query, {}, { sort: { trashedAt: -1 } });\n const plainItems = withSignedUrls(await Promise.all(items.map(item => item.toClient())), config);\n\n res.status(200).json({ status: 200, message: 'Trash items', data: { items: plainItems, hasMore: false } });\n return;\n }\n\n case 'restore': {\n const restoreData = schemas.deleteQuerySchema.safeParse(req.query);\n if (!restoreData.success) return void res.status(400).json({ status: 400, message: 'Could not restore: invalid ID' });\n const { id } = restoreData.data;\n const drive = await Drive.findById(id);\n if (!drive) return void res.status(404).json({ status: 404, message: 'Could not restore: item not found' });\n\n let targetParentId = drive.parentId;\n if (targetParentId) {\n const parent = await Drive.findById(targetParentId);\n if (parent?.trashedAt) {\n targetParentId = null;\n }\n }\n\n const itemProvider = drive.provider?.type === 'GOOGLE' ? GoogleDriveProvider : LocalStorageProvider;\n const itemAccountId = drive.storageAccountId ? drive.storageAccountId.toString() : undefined;\n\n try {\n await itemProvider.untrash([id], owner, itemAccountId);\n if (targetParentId !== drive.parentId) {\n await itemProvider.move(id, targetParentId?.toString() ?? null, owner, itemAccountId);\n }\n } catch (e) {\n console.error('Provider restore failed:', e);\n }\n\n drive.trashedAt = null;\n drive.parentId = targetParentId;\n await drive.save();\n\n res.status(200).json({\n status: 200,\n message: targetParentId === null && drive.parentId !== null ? 'Restored to root (parent folder was trashed)' : 'Restored',\n data: null,\n });\n return;\n }\n\n case 'move': {\n const moveData = schemas.moveBodySchema.safeParse(req.body);\n if (!moveData.success) return void res.status(400).json({ status: 400, message: 'Could not move: invalid request data' });\n const { ids, targetFolderId } = moveData.data;\n\n const items: TDatabaseDrive[] = [];\n const effectiveTargetId = targetFolderId === 'root' || !targetFolderId ? null : targetFolderId;\n\n for (const id of ids) {\n try {\n const item = await provider.move(id, effectiveTargetId, owner, accountId);\n items.push(item);\n } catch (e) {\n console.error(`Failed to move item ${id}`, e);\n }\n }\n\n res.status(200).json({ status: 200, message: 'Moved', data: { items: withSignedUrls(items, config) } });\n return;\n }\n\n case 'reorder': {\n if (req.method !== 'POST') {\n return void res.status(405).json({ status: 405, message: 'Reordering requires a POST request' });\n }\n\n const reorderData = schemas.reorderBodySchema.safeParse(req.body);\n if (!reorderData.success) {\n return void res.status(400).json({ status: 400, message: 'Could not reorder: invalid request data' });\n }\n\n const { ids } = reorderData.data;\n\n const query: Record<string, unknown> = {\n _id: { $in: ids },\n 'provider.type': provider.name,\n storageAccountId: accountId || null,\n trashedAt: null,\n };\n\n if (!isRootMode) {\n query.owner = owner;\n }\n\n const existingItems = await Drive.find(query, { _id: 1, parentId: 1 });\n if (existingItems.length !== ids.length) {\n return void res.status(404).json({ status: 404, message: 'Could not reorder: one or more items were not found' });\n }\n\n const parentIds = new Set(existingItems.map(item => (item.parentId ? item.parentId.toString() : 'root')));\n if (parentIds.size > 1) {\n return void res.status(400).json({ status: 400, message: 'Could not reorder: all items must be in the same folder' });\n }\n\n const operations = ids.map((id, order) => ({\n updateOne: {\n filter: {\n _id: id,\n 'provider.type': provider.name,\n storageAccountId: accountId || null,\n trashedAt: null,\n ...(isRootMode ? {} : { owner }),\n },\n update: { $set: { order } },\n },\n }));\n\n await Drive.bulkWrite(operations as never);\n\n const updatedItems = await Drive.find(query, {}, { sort: { order: 1 } });\n const plainItems = withSignedUrls(await Promise.all(updatedItems.map(item => item.toClient())), config);\n\n res.status(200).json({ status: 200, message: 'Reordered', data: { items: plainItems } });\n return;\n }\n\n case 'rename': {\n const renameData = schemas.renameBodySchema.safeParse({ id: req.query.id, ...req.body });\n if (!renameData.success) return void res.status(400).json({ status: 400, message: 'Could not rename: invalid request data' });\n const { id, newName } = renameData.data;\n const item = withSignedUrl(await provider.rename(id, newName, owner, accountId), config);\n res.status(200).json({ status: 200, message: 'Renamed', data: { item } });\n return;\n }\n\n default: {\n res.status(400).json({ status: 400, message: `Unknown action requested: \"${action}\"` });\n return;\n }\n }\n};\n","import type { NextApiRequest, NextApiResponse } from 'next';\n\nimport { getDriveConfig, getDriveInformation, driveConfiguration } from '@/server/config';\nimport { applyCorsHeaders } from '@/server/actions/cors';\nimport { handlePublicAction } from '@/server/actions/public';\nimport { handleAuthAction } from '@/server/actions/auth';\nimport { handleDriveAction } from '@/server/actions/drive';\nimport { resolveProvider } from '@/server/actions/shared';\n\n// ** Parse the raw request body as JSON.\n// ** Needed because Next.js Pages Router disables the built-in body parser (config.api.bodyParser = false)\n// ** so that multipart/form-data uploads can be streamed directly to formidable. That leaves `req.body`\n// ** as `undefined` for every other (JSON) action unless we parse it ourselves here.\n// ** Only safe to call for Node-stream-based requests (Next.js, Express); other adapters (e.g. Hono)\n// ** don't expose `.on()` and are expected to populate `req.body` themselves before calling the handler.\nconst parseJsonBody = (req: NextApiRequest): Promise<Record<string, unknown>> => {\n return new Promise((resolve) => {\n try {\n const chunks: Buffer[] = [];\n let totalBytes = 0;\n req.on('data', (chunk: Buffer) => {\n totalBytes += chunk.length;\n // ** Safety net: no JSON action in this API needs anywhere near this much body -\n // ** bail out rather than buffering an unbounded payload into memory.\n if (totalBytes > 1024 * 1024) {\n req.destroy();\n return resolve({});\n }\n chunks.push(chunk);\n });\n req.on('end', () => {\n const raw = Buffer.concat(chunks).toString();\n if (!raw) return resolve({});\n try {\n resolve(JSON.parse(raw));\n } catch {\n resolve({});\n }\n });\n req.on('error', () => resolve({}));\n } catch {\n resolve({});\n }\n });\n};\n\n// ** Main API handler for all drive operations\nexport const driveAPIHandler = async (req: NextApiRequest, res: NextApiResponse): Promise<void> => {\n // ** Only parse manually when the body hasn't already been parsed (e.g. by Express's json middleware)\n // ** and the request object actually supports Node-style streaming (`.on`). Frameworks that already\n // ** populate `req.body` (even with `{}`) or don't expose `.on()` are left untouched.\n if (req.body === undefined && typeof req.on === 'function' && (req.headers['content-type'] || '').includes('application/json')) {\n req.body = await parseJsonBody(req);\n } else if (req.body === undefined) {\n req.body = {};\n }\n\n\n const action = (req.query.action as string) || (req.query.code && req.query.state ? 'callback' : undefined);\n\n let config: ReturnType<typeof getDriveConfig>;\n try {\n config = getDriveConfig();\n } catch (error) {\n console.error('[next-drive] Configuration error:', error);\n res.status(500).json({ status: 500, message: 'Drive is not ready: failed to initialize configuration' });\n return;\n }\n\n const isPreflightHandled = applyCorsHeaders(req, res, config);\n if (isPreflightHandled) return;\n\n if (!action) {\n res.status(400).json({ status: 400, message: 'Missing \"action\" parameter in request' });\n return;\n }\n\n const wasPublicHandled = await handlePublicAction(req, res, action, config);\n if (wasPublicHandled) return;\n\n try {\n const mode = config.mode || 'NORMAL';\n\n if (action === 'information') {\n const { clientId, clientSecret, redirectUri } = config.storage?.google || {};\n const googleConfigured = !!(clientId && clientSecret && redirectUri);\n\n let authenticated = false;\n try {\n await getDriveInformation({ method: 'REQUEST', req });\n authenticated = true;\n } catch {\n authenticated = false;\n }\n\n res.status(200).json({\n status: 200,\n message: 'Information retrieved',\n data: {\n providers: {\n google: googleConfigured,\n },\n mode,\n authenticated,\n unauthenticatedUploads: !!config.security?.unauthenticated?.enabled,\n },\n });\n return;\n }\n\n const isRootMode = mode === 'ROOT';\n\n // Resolve the caller. Anonymous visitors are allowed only for one-time uploads\n // (when enabled); every other action keeps requiring authentication.\n let information: Awaited<ReturnType<typeof getDriveInformation>>;\n let authenticated = true;\n try {\n information = await getDriveInformation({ method: 'REQUEST', req });\n } catch (err) {\n if (action === 'upload' && config.security?.unauthenticated?.enabled) {\n information = { key: null, storage: { quotaInBytes: 0 } };\n authenticated = false;\n } else {\n throw err;\n }\n }\n const { key: owner } = information;\n\n const wasAuthHandled = await handleAuthAction(req, res, action, config, owner);\n if (wasAuthHandled) return;\n\n const { provider, accountId } = await resolveProvider(req, owner);\n\n await handleDriveAction({\n req,\n res,\n action,\n config,\n owner,\n isRootMode,\n authenticated,\n information,\n provider,\n accountId,\n });\n } catch (error: unknown) {\n console.error(`[next-drive] Error handling action ${action}:`, error);\n const detail = error instanceof Error ? error.message : 'Something went wrong while processing your request';\n res.status(500).json({ status: 500, message: `Request \"${action}\" failed: ${detail}` });\n }\n};\n\nexport { driveConfiguration, getDriveConfig, getDriveInformation };\nexport { driveGetUrl, driveReadFile, driveFilePath, driveUpload, driveDelete, driveList, driveInfo, driveListFiles, driveCleanup, driveConfirm, drivePurgeExpired } from '@/server/controllers/drive';\nexport { default as DatabaseMongoDrive } from '@/server/database/mongoose/schema/drive';\nexport type { IDatabaseDriveDocument } from '@/server/database/mongoose/schema/drive';\nexport { driveFileSchemaZod } from '@/server/zod/schemas';\nexport { driveCreateUrl } from '@/client/utils';\nexport type { TDriveFile, TDriveInformation } from '@/types/client';\nexport type {\n TDriveDatabase,\n TDriveMode,\n TDriveInformationInput,\n TDriveConfigInformation,\n TDriveSecurityConfig,\n TDriveStorageConfig,\n TDriveCorsConfig,\n TDriveConfiguration,\n TDatabaseDriveStatus,\n TDatabaseDriveMetadataFile,\n TDatabaseDriveMetadataFolder,\n TDatabaseDriveInformation,\n TDatabaseDriveProvider,\n TDatabaseDriveMetadata,\n TDatabaseDrive,\n TDriveAction,\n TDriveAPIResponse,\n TDriveThumbnailSize,\n TDriveImageQuality,\n TMigration,\n TImageMetadata,\n TVideoMetadata,\n} from '@/types/server';\n"]}
|
|
@@ -263,7 +263,7 @@ var getGlobal = () => {
|
|
|
263
263
|
config: null,
|
|
264
264
|
migrationPromise: null,
|
|
265
265
|
initialized: false,
|
|
266
|
-
abuse: { ipHits: /* @__PURE__ */ new Map(), active: /* @__PURE__ */ new Map(), lastSweep: 0 }
|
|
266
|
+
abuse: { ipHits: /* @__PURE__ */ new Map(), active: /* @__PURE__ */ new Map(), lastSweep: 0, transformActive: 0 }
|
|
267
267
|
};
|
|
268
268
|
}
|
|
269
269
|
return globalThis[GLOBAL_KEY];
|
|
@@ -293,6 +293,9 @@ var driveConfiguration = async (config) => {
|
|
|
293
293
|
allowedMimeTypes: config.security?.allowedMimeTypes ?? ["*/*"],
|
|
294
294
|
signedUrls: config.security?.signedUrls,
|
|
295
295
|
trash: config.security?.trash,
|
|
296
|
+
publicRateLimit: config.security?.publicRateLimit,
|
|
297
|
+
transformConcurrency: config.security?.transformConcurrency,
|
|
298
|
+
cacheMaxAgeDays: config.security?.cacheMaxAgeDays,
|
|
296
299
|
unauthenticated: config.security?.unauthenticated
|
|
297
300
|
}
|
|
298
301
|
};
|
|
@@ -312,11 +315,19 @@ var driveConfiguration = async (config) => {
|
|
|
312
315
|
allowedMimeTypes: config.security?.allowedMimeTypes ?? ["*/*"],
|
|
313
316
|
signedUrls: config.security?.signedUrls,
|
|
314
317
|
trash: config.security?.trash,
|
|
318
|
+
publicRateLimit: config.security?.publicRateLimit,
|
|
319
|
+
transformConcurrency: config.security?.transformConcurrency,
|
|
320
|
+
cacheMaxAgeDays: config.security?.cacheMaxAgeDays,
|
|
315
321
|
unauthenticated: config.security?.unauthenticated
|
|
316
322
|
},
|
|
317
323
|
information: config.information
|
|
318
324
|
};
|
|
319
325
|
}
|
|
326
|
+
if (!g.config.security?.signedUrls?.enabled && !g.config.security?.publicRateLimit) {
|
|
327
|
+
console.warn(
|
|
328
|
+
'[next-drive] security.signedUrls is disabled and security.publicRateLimit is unset - "?action=serve"/"thumbnail" will serve any valid file id to anyone, with no rate limit. Enable security.signedUrls (recommended) and/or set security.publicRateLimit if these files should not be fully public.'
|
|
329
|
+
);
|
|
330
|
+
}
|
|
320
331
|
g.initialized = true;
|
|
321
332
|
if (!g.migrationPromise) {
|
|
322
333
|
g.migrationPromise = runMigrations(resolvedPath);
|
|
@@ -963,14 +974,36 @@ var GoogleDriveProvider = {
|
|
|
963
974
|
}
|
|
964
975
|
},
|
|
965
976
|
openStream: async (item, accountId) => {
|
|
977
|
+
if (item.information.type === "FOLDER") throw new Error("Could not open Google Drive file: folders cannot be streamed");
|
|
978
|
+
const cachePath = path__default.default.join(getDriveConfig().storage.path, "file", item._id.toString(), "data.bin");
|
|
979
|
+
if (fs__default.default.existsSync(cachePath)) {
|
|
980
|
+
return {
|
|
981
|
+
stream: fs__default.default.createReadStream(cachePath),
|
|
982
|
+
mime: item.information.mime,
|
|
983
|
+
size: fs__default.default.statSync(cachePath).size
|
|
984
|
+
};
|
|
985
|
+
}
|
|
966
986
|
const { client } = await createAuthClient(item.owner, accountId || item.storageAccountId?.toString());
|
|
967
987
|
const drive = googleapis.google.drive({ version: "v3", auth: client });
|
|
968
988
|
if (!item.provider?.google?.id) throw new Error("Could not open Google Drive file: its Google file ID is missing");
|
|
969
|
-
if (item.information.type === "FOLDER") throw new Error("Could not open Google Drive file: folders cannot be streamed");
|
|
970
989
|
const res = await drive.files.get(
|
|
971
990
|
{ fileId: item.provider.google.id, alt: "media" },
|
|
972
991
|
{ responseType: "stream" }
|
|
973
992
|
);
|
|
993
|
+
const fileDir = path__default.default.dirname(cachePath);
|
|
994
|
+
if (!fs__default.default.existsSync(fileDir)) fs__default.default.mkdirSync(fileDir, { recursive: true });
|
|
995
|
+
const tempPath = `${cachePath}.tmp`;
|
|
996
|
+
const writeStream = fs__default.default.createWriteStream(tempPath);
|
|
997
|
+
res.data.pipe(writeStream);
|
|
998
|
+
writeStream.on("finish", () => {
|
|
999
|
+
try {
|
|
1000
|
+
fs__default.default.renameSync(tempPath, cachePath);
|
|
1001
|
+
} catch {
|
|
1002
|
+
fs__default.default.unlinkSync(tempPath);
|
|
1003
|
+
}
|
|
1004
|
+
});
|
|
1005
|
+
writeStream.on("error", () => fs__default.default.unlink(tempPath, () => {
|
|
1006
|
+
}));
|
|
974
1007
|
return {
|
|
975
1008
|
stream: res.data,
|
|
976
1009
|
mime: item.information.mime,
|
|
@@ -1718,7 +1751,7 @@ var driveCleanup = async () => {
|
|
|
1718
1751
|
}
|
|
1719
1752
|
}
|
|
1720
1753
|
try {
|
|
1721
|
-
const { getTusServer: getTusServer2 } = await import('./tus-
|
|
1754
|
+
const { getTusServer: getTusServer2 } = await import('./tus-5LLFMVQK.cjs');
|
|
1722
1755
|
await getTusServer2().cleanUpExpiredUploads();
|
|
1723
1756
|
} catch (e) {
|
|
1724
1757
|
console.error("[next-drive] Failed to clean up expired tus uploads:", e);
|
|
@@ -1778,6 +1811,12 @@ var countActiveAnon = (store) => {
|
|
|
1778
1811
|
return store.active.size;
|
|
1779
1812
|
};
|
|
1780
1813
|
var releaseSlot = (id) => abuseStore()?.active.delete(id);
|
|
1814
|
+
var resolveTusCorsOptions = (cors) => {
|
|
1815
|
+
if (!cors?.enabled) return { allowedCredentials: false };
|
|
1816
|
+
const { origins, credentials = false } = cors;
|
|
1817
|
+
if (!origins || origins === "*") return { allowedCredentials: credentials };
|
|
1818
|
+
return { allowedOrigins: Array.isArray(origins) ? origins : [origins], allowedCredentials: credentials };
|
|
1819
|
+
};
|
|
1781
1820
|
var getTusServer = () => {
|
|
1782
1821
|
const g = globalThis;
|
|
1783
1822
|
g.__nextDrive = g.__nextDrive ?? {};
|
|
@@ -1786,6 +1825,7 @@ var getTusServer = () => {
|
|
|
1786
1825
|
const tusDir = path__default.default.join(config.storage.path, "tus");
|
|
1787
1826
|
fs__default.default.mkdirSync(tusDir, { recursive: true });
|
|
1788
1827
|
const apiPath = new URL(config.apiUrl || "/api/drive", "http://x").pathname;
|
|
1828
|
+
const { allowedOrigins, allowedCredentials } = resolveTusCorsOptions(config.cors);
|
|
1789
1829
|
const server$1 = new server.Server({
|
|
1790
1830
|
path: apiPath,
|
|
1791
1831
|
datastore: new fileStore.FileStore({ directory: tusDir, expirationPeriodInMilliseconds: UPLOAD_EXPIRATION_MS }),
|
|
@@ -1793,6 +1833,8 @@ var getTusServer = () => {
|
|
|
1793
1833
|
// ** must be declared here to survive cross-origin requests.
|
|
1794
1834
|
allowedHeaders: ["X-Drive-Account"],
|
|
1795
1835
|
exposedHeaders: ["X-Drive-Item"],
|
|
1836
|
+
allowedOrigins,
|
|
1837
|
+
allowedCredentials,
|
|
1796
1838
|
maxSize: (req) => {
|
|
1797
1839
|
const ctx = getCtx(req);
|
|
1798
1840
|
if (!ctx) return Number.MAX_SAFE_INTEGER;
|
|
@@ -1832,7 +1874,7 @@ var getTusServer = () => {
|
|
|
1832
1874
|
}
|
|
1833
1875
|
}
|
|
1834
1876
|
const nreq = req.__nodeReq;
|
|
1835
|
-
const ip = (nreq && abuse.clientId?.(nreq)) ?? (abuse.trustedHeaders ?? ["cf-connecting-ip", "x-forwarded-for"]).map((h) => req.headers.get(h)).find(Boolean)?.split(",")[0].trim() ?? nreq?.socket?.remoteAddress ?? "unknown";
|
|
1877
|
+
const ip = (nreq && abuse.clientId?.(nreq)) ?? (abuse.trustedHeaders ?? ["cf-connecting-ip", "x-real-ip", "x-forwarded-for"]).map((h) => req.headers.get(h)).find(Boolean)?.split(",")[0].trim() ?? nreq?.socket?.remoteAddress ?? "unknown";
|
|
1836
1878
|
const hits = (store.ipHits.get(ip) ?? []).filter((t) => now - t < 36e5);
|
|
1837
1879
|
const perIp = abuse.perIp;
|
|
1838
1880
|
if (perIp && hits.filter((t) => now - t < perIp.windowMinutes * 6e4).length >= perIp.max) throw { status_code: 429, body: "Too many uploads, please try again later" };
|
|
@@ -1976,7 +2018,8 @@ exports.getImageSettings = getImageSettings;
|
|
|
1976
2018
|
exports.getTusServer = getTusServer;
|
|
1977
2019
|
exports.handleUpload = handleUpload;
|
|
1978
2020
|
exports.resolveProvider = resolveProvider;
|
|
2021
|
+
exports.resolveTusCorsOptions = resolveTusCorsOptions;
|
|
1979
2022
|
exports.withSignedUrl = withSignedUrl;
|
|
1980
2023
|
exports.withSignedUrls = withSignedUrls;
|
|
1981
|
-
//# sourceMappingURL=chunk-
|
|
1982
|
-
//# sourceMappingURL=chunk-
|
|
2024
|
+
//# sourceMappingURL=chunk-HWNIRA6Y.cjs.map
|
|
2025
|
+
//# sourceMappingURL=chunk-HWNIRA6Y.cjs.map
|