@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.
Files changed (39) hide show
  1. package/dist/{chunk-J2OYAYYA.js → chunk-2DCZENJ2.js} +116 -10
  2. package/dist/chunk-2DCZENJ2.js.map +1 -0
  3. package/dist/{chunk-LTWWPQJA.cjs → chunk-HWNIRA6Y.cjs} +49 -6
  4. package/dist/chunk-HWNIRA6Y.cjs.map +1 -0
  5. package/dist/{chunk-DNJRZKAF.js → chunk-IWV6G7HQ.js} +49 -7
  6. package/dist/chunk-IWV6G7HQ.js.map +1 -0
  7. package/dist/{chunk-QE5EHVVA.cjs → chunk-WUG4ATLH.cjs} +150 -44
  8. package/dist/chunk-WUG4ATLH.cjs.map +1 -0
  9. package/dist/server/actions/public.d.ts.map +1 -1
  10. package/dist/server/config.d.ts.map +1 -1
  11. package/dist/server/express.cjs +12 -12
  12. package/dist/server/express.js +4 -4
  13. package/dist/server/hono.cjs +12 -12
  14. package/dist/server/hono.js +4 -4
  15. package/dist/server/index.cjs +19 -19
  16. package/dist/server/index.d.ts.map +1 -1
  17. package/dist/server/index.js +2 -2
  18. package/dist/server/storage-adapters/google.d.ts.map +1 -1
  19. package/dist/server/tus.d.ts +5 -1
  20. package/dist/server/tus.d.ts.map +1 -1
  21. package/dist/tus-5LLFMVQK.cjs +20 -0
  22. package/dist/tus-5LLFMVQK.cjs.map +1 -0
  23. package/dist/tus-C4T5ZDTG.js +3 -0
  24. package/dist/tus-C4T5ZDTG.js.map +1 -0
  25. package/dist/types/server/config.d.ts +8 -0
  26. package/dist/types/server/config.d.ts.map +1 -1
  27. package/dist/types/server/express.d.ts +3 -24
  28. package/dist/types/server/express.d.ts.map +1 -1
  29. package/dist/types/server/hono.d.ts +3 -24
  30. package/dist/types/server/hono.d.ts.map +1 -1
  31. package/package.json +1 -1
  32. package/dist/chunk-DNJRZKAF.js.map +0 -1
  33. package/dist/chunk-J2OYAYYA.js.map +0 -1
  34. package/dist/chunk-LTWWPQJA.cjs.map +0 -1
  35. package/dist/chunk-QE5EHVVA.cjs.map +0 -1
  36. package/dist/tus-35IUZMFV.js +0 -3
  37. package/dist/tus-35IUZMFV.js.map +0 -1
  38. package/dist/tus-PK6UHGE4.cjs +0 -16
  39. 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-DNJRZKAF.js';
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 stream2 = await itemProvider.getThumbnail(drive, itemAccountId);
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 quality = req.query.quality;
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).pipe(res);
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
- res.setHeader("Cache-Control", "public, max-age=31536000, immutable");
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
- req.on("data", (chunk) => chunks.push(chunk));
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-J2OYAYYA.js.map
756
- //# sourceMappingURL=chunk-J2OYAYYA.js.map
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-PK6UHGE4.cjs');
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-LTWWPQJA.cjs.map
1982
- //# sourceMappingURL=chunk-LTWWPQJA.cjs.map
2024
+ //# sourceMappingURL=chunk-HWNIRA6Y.cjs.map
2025
+ //# sourceMappingURL=chunk-HWNIRA6Y.cjs.map