@muhgholy/next-drive 3.0.0 → 3.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +91 -50
- package/dist/{chunk-KCDI2FBD.js → chunk-F5ZVJGYN.js} +132 -13
- package/dist/chunk-F5ZVJGYN.js.map +1 -0
- package/dist/client/components/drive/sidebar.d.ts.map +1 -1
- package/dist/client/components/drive/upload.d.ts.map +1 -1
- package/dist/client/file-chooser.d.ts.map +1 -1
- package/dist/client/hooks/useUpload.d.ts.map +1 -1
- package/dist/client/index.js +171 -21
- package/dist/client/index.js.map +1 -1
- package/dist/server/controllers/drive.d.ts +29 -0
- package/dist/server/controllers/drive.d.ts.map +1 -1
- package/dist/server/express.js +2 -2
- package/dist/server/index.d.ts +1 -1
- package/dist/server/index.d.ts.map +1 -1
- package/dist/server/index.js +1 -1
- package/dist/server/providers/local.d.ts.map +1 -1
- package/dist/types/client/index.d.ts +5 -0
- package/dist/types/client/index.d.ts.map +1 -1
- package/package.json +103 -102
- package/dist/chunk-KCDI2FBD.js.map +0 -1
package/README.md
CHANGED
|
@@ -4,13 +4,13 @@ File storage and management for Next.js and Express apps. Includes a responsive
|
|
|
4
4
|
|
|
5
5
|
## Features
|
|
6
6
|
|
|
7
|
-
-
|
|
8
|
-
-
|
|
9
|
-
-
|
|
10
|
-
-
|
|
11
|
-
-
|
|
12
|
-
-
|
|
13
|
-
-
|
|
7
|
+
- 📁 **File Management** – Upload, rename, move, organize files and folders
|
|
8
|
+
- 🔍 **Search** – Search active files or trash with real-time filtering
|
|
9
|
+
- 🗑️ **Trash System** – Soft delete, restore, and empty trash
|
|
10
|
+
- 📱 **Responsive UI** – Optimized for desktop and mobile
|
|
11
|
+
- 🎬 **Video Thumbnails** – Auto-generated thumbnails (requires FFmpeg)
|
|
12
|
+
- 🔐 **Security** – Signed URLs and configurable upload limits
|
|
13
|
+
- 📊 **View Modes** – Grid/List views with sorting and grouping
|
|
14
14
|
|
|
15
15
|
---
|
|
16
16
|
|
|
@@ -49,7 +49,7 @@ Add the package to your Tailwind content config:
|
|
|
49
49
|
```js
|
|
50
50
|
// tailwind.config.js
|
|
51
51
|
export default {
|
|
52
|
-
content: [
|
|
52
|
+
content: ["./app/**/*.{js,ts,jsx,tsx,mdx}", "./pages/**/*.{js,ts,jsx,tsx,mdx}", "./components/**/*.{js,ts,jsx,tsx,mdx}", "./node_modules/@muhgholy/next-drive/dist/**/*.{js,mjs}"],
|
|
53
53
|
};
|
|
54
54
|
```
|
|
55
55
|
|
|
@@ -65,16 +65,16 @@ Create `lib/drive.ts` to configure storage, security, and authentication:
|
|
|
65
65
|
|
|
66
66
|
```typescript
|
|
67
67
|
// lib/drive.ts
|
|
68
|
-
import { driveConfiguration } from
|
|
69
|
-
import type { TDriveConfigInformation } from
|
|
68
|
+
import { driveConfiguration } from "@muhgholy/next-drive/server";
|
|
69
|
+
import type { TDriveConfigInformation } from "@muhgholy/next-drive/server";
|
|
70
70
|
|
|
71
71
|
driveConfiguration({
|
|
72
|
-
database:
|
|
73
|
-
apiUrl:
|
|
74
|
-
storage: { path:
|
|
72
|
+
database: "MONGOOSE",
|
|
73
|
+
apiUrl: "/api/drive",
|
|
74
|
+
storage: { path: "/var/data/drive" },
|
|
75
75
|
security: {
|
|
76
76
|
maxUploadSizeInBytes: 50 * 1024 * 1024, // 50MB
|
|
77
|
-
allowedMimeTypes: [
|
|
77
|
+
allowedMimeTypes: ["image/*", "video/*", "application/pdf"],
|
|
78
78
|
signedUrls: {
|
|
79
79
|
enabled: true,
|
|
80
80
|
secret: process.env.DRIVE_SECRET!,
|
|
@@ -83,7 +83,7 @@ driveConfiguration({
|
|
|
83
83
|
},
|
|
84
84
|
information: async (req): Promise<TDriveConfigInformation> => {
|
|
85
85
|
const auth = await verifyAuth(req);
|
|
86
|
-
if (!auth) throw new Error(
|
|
86
|
+
if (!auth) throw new Error("Unauthenticated");
|
|
87
87
|
return {
|
|
88
88
|
key: { userId: auth.userId },
|
|
89
89
|
storage: { quotaInBytes: 1024 * 1024 * 1024 }, // 1GB
|
|
@@ -98,13 +98,13 @@ driveConfiguration({
|
|
|
98
98
|
|
|
99
99
|
```typescript
|
|
100
100
|
// pages/api/drive.ts
|
|
101
|
-
import
|
|
102
|
-
import { driveAPIHandler } from
|
|
103
|
-
import type { NextApiRequest, NextApiResponse } from
|
|
101
|
+
import "@/lib/drive";
|
|
102
|
+
import { driveAPIHandler } from "@muhgholy/next-drive/server";
|
|
103
|
+
import type { NextApiRequest, NextApiResponse } from "next";
|
|
104
104
|
|
|
105
105
|
export default async function handler(req: NextApiRequest, res: NextApiResponse) {
|
|
106
106
|
// Parse JSON body manually (body parser is disabled)
|
|
107
|
-
if (req.headers[
|
|
107
|
+
if (req.headers["content-type"]?.includes("application/json")) {
|
|
108
108
|
const chunks: Buffer[] = [];
|
|
109
109
|
for await (const chunk of req) chunks.push(chunk);
|
|
110
110
|
const buffer = Buffer.concat(chunks);
|
|
@@ -127,10 +127,10 @@ Wrap your app with `DriveProvider`:
|
|
|
127
127
|
|
|
128
128
|
```tsx
|
|
129
129
|
// app/layout.tsx
|
|
130
|
-
import { DriveProvider } from
|
|
130
|
+
import { DriveProvider } from "@muhgholy/next-drive/client";
|
|
131
131
|
|
|
132
132
|
export default function RootLayout({ children }) {
|
|
133
|
-
return <DriveProvider apiEndpoint=
|
|
133
|
+
return <DriveProvider apiEndpoint="/api/drive">{children}</DriveProvider>;
|
|
134
134
|
}
|
|
135
135
|
```
|
|
136
136
|
|
|
@@ -139,7 +139,7 @@ export default function RootLayout({ children }) {
|
|
|
139
139
|
**File Explorer:**
|
|
140
140
|
|
|
141
141
|
```tsx
|
|
142
|
-
import { DriveExplorer } from
|
|
142
|
+
import { DriveExplorer } from "@muhgholy/next-drive/client";
|
|
143
143
|
|
|
144
144
|
export default function DrivePage() {
|
|
145
145
|
return <DriveExplorer />;
|
|
@@ -149,13 +149,13 @@ export default function DrivePage() {
|
|
|
149
149
|
**File Picker (for forms):**
|
|
150
150
|
|
|
151
151
|
```tsx
|
|
152
|
-
import { useState } from
|
|
153
|
-
import { DriveFileChooser } from
|
|
154
|
-
import type { TDriveFile } from
|
|
152
|
+
import { useState } from "react";
|
|
153
|
+
import { DriveFileChooser } from "@muhgholy/next-drive/client";
|
|
154
|
+
import type { TDriveFile } from "@muhgholy/next-drive/client";
|
|
155
155
|
|
|
156
156
|
function MyForm() {
|
|
157
157
|
const [file, setFile] = useState<TDriveFile | null>(null);
|
|
158
|
-
return <DriveFileChooser value={file} onChange={setFile} accept=
|
|
158
|
+
return <DriveFileChooser value={file} onChange={setFile} accept="image/*" />;
|
|
159
159
|
}
|
|
160
160
|
```
|
|
161
161
|
|
|
@@ -167,20 +167,20 @@ Use the Express adapter instead of Next.js API routes:
|
|
|
167
167
|
|
|
168
168
|
```typescript
|
|
169
169
|
// lib/drive.ts
|
|
170
|
-
import { driveConfigurationExpress } from
|
|
171
|
-
import type { TDriveConfigInformation } from
|
|
170
|
+
import { driveConfigurationExpress } from "@muhgholy/next-drive/server/express";
|
|
171
|
+
import type { TDriveConfigInformation } from "@muhgholy/next-drive/server/express";
|
|
172
172
|
|
|
173
173
|
driveConfigurationExpress({
|
|
174
|
-
database:
|
|
175
|
-
apiUrl:
|
|
176
|
-
storage: { path:
|
|
174
|
+
database: "MONGOOSE",
|
|
175
|
+
apiUrl: "/api/drive",
|
|
176
|
+
storage: { path: "/var/data/drive" },
|
|
177
177
|
security: {
|
|
178
178
|
maxUploadSizeInBytes: 50 * 1024 * 1024,
|
|
179
|
-
allowedMimeTypes: [
|
|
179
|
+
allowedMimeTypes: ["image/*", "video/*", "application/pdf"],
|
|
180
180
|
},
|
|
181
181
|
information: async (req): Promise<TDriveConfigInformation> => {
|
|
182
182
|
const auth = await verifyAuth(req);
|
|
183
|
-
if (!auth) throw new Error(
|
|
183
|
+
if (!auth) throw new Error("Unauthenticated");
|
|
184
184
|
return {
|
|
185
185
|
key: { userId: auth.userId },
|
|
186
186
|
storage: { quotaInBytes: 1024 * 1024 * 1024 },
|
|
@@ -191,12 +191,12 @@ driveConfigurationExpress({
|
|
|
191
191
|
|
|
192
192
|
```typescript
|
|
193
193
|
// routes/drive.ts
|
|
194
|
-
import
|
|
195
|
-
import express from
|
|
196
|
-
import { driveAPIHandlerExpress } from
|
|
194
|
+
import "./lib/drive";
|
|
195
|
+
import express from "express";
|
|
196
|
+
import { driveAPIHandlerExpress } from "@muhgholy/next-drive/server/express";
|
|
197
197
|
|
|
198
198
|
const router = express.Router();
|
|
199
|
-
router.all(
|
|
199
|
+
router.all("/drive", driveAPIHandlerExpress);
|
|
200
200
|
|
|
201
201
|
export default router;
|
|
202
202
|
```
|
|
@@ -210,8 +210,8 @@ export default router;
|
|
|
210
210
|
Validate file data in forms or API routes:
|
|
211
211
|
|
|
212
212
|
```typescript
|
|
213
|
-
import { z } from
|
|
214
|
-
import { driveFileSchemaZod } from
|
|
213
|
+
import { z } from "zod";
|
|
214
|
+
import { driveFileSchemaZod } from "@muhgholy/next-drive/schemas";
|
|
215
215
|
|
|
216
216
|
const formSchema = z.object({
|
|
217
217
|
asset: driveFileSchemaZod,
|
|
@@ -228,8 +228,8 @@ const formSchema = z.object({
|
|
|
228
228
|
Generate URLs for displaying files:
|
|
229
229
|
|
|
230
230
|
```tsx
|
|
231
|
-
import { useDrive } from
|
|
232
|
-
import type { TDriveFile } from
|
|
231
|
+
import { useDrive } from "@muhgholy/next-drive/client";
|
|
232
|
+
import type { TDriveFile } from "@muhgholy/next-drive/client";
|
|
233
233
|
|
|
234
234
|
function MyComponent({ driveFile }: { driveFile: TDriveFile }) {
|
|
235
235
|
const { createUrl, createSrcSet } = useDrive();
|
|
@@ -238,10 +238,10 @@ function MyComponent({ driveFile }: { driveFile: TDriveFile }) {
|
|
|
238
238
|
const url = createUrl(driveFile);
|
|
239
239
|
|
|
240
240
|
// With quality and format
|
|
241
|
-
const optimizedUrl = createUrl(driveFile, { quality:
|
|
241
|
+
const optimizedUrl = createUrl(driveFile, { quality: "medium", format: "webp" });
|
|
242
242
|
|
|
243
243
|
// Responsive srcSet for images
|
|
244
|
-
const { srcSet, sizes } = createSrcSet(driveFile,
|
|
244
|
+
const { srcSet, sizes } = createSrcSet(driveFile, "webp");
|
|
245
245
|
|
|
246
246
|
return <img src={optimizedUrl} srcSet={srcSet} sizes={sizes} alt={driveFile.file.name} />;
|
|
247
247
|
}
|
|
@@ -251,10 +251,51 @@ function MyComponent({ driveFile }: { driveFile: TDriveFile }) {
|
|
|
251
251
|
|
|
252
252
|
## Server-Side File Access
|
|
253
253
|
|
|
254
|
+
### Upload File
|
|
255
|
+
|
|
256
|
+
Upload files programmatically from server-side code:
|
|
257
|
+
|
|
258
|
+
```typescript
|
|
259
|
+
import { driveUpload } from "@muhgholy/next-drive/server";
|
|
260
|
+
|
|
261
|
+
// Upload from file path
|
|
262
|
+
const file = await driveUpload(
|
|
263
|
+
"/tmp/photo.jpg",
|
|
264
|
+
{ userId: "123" },
|
|
265
|
+
{
|
|
266
|
+
name: "photo.jpg",
|
|
267
|
+
parentId: "folderId", // Optional: folder ID or null for root
|
|
268
|
+
accountId: "LOCAL", // Optional: storage account ID
|
|
269
|
+
enforce: false, // Optional: bypass quota check
|
|
270
|
+
}
|
|
271
|
+
);
|
|
272
|
+
|
|
273
|
+
// Upload from stream
|
|
274
|
+
import fs from "fs";
|
|
275
|
+
const stream = fs.createReadStream("/tmp/video.mp4");
|
|
276
|
+
const file = await driveUpload(
|
|
277
|
+
stream,
|
|
278
|
+
{ userId: "123" },
|
|
279
|
+
{
|
|
280
|
+
name: "video.mp4",
|
|
281
|
+
enforce: true, // Skip quota check
|
|
282
|
+
}
|
|
283
|
+
);
|
|
284
|
+
```
|
|
285
|
+
|
|
286
|
+
**Options:**
|
|
287
|
+
|
|
288
|
+
| Option | Type | Required | Description |
|
|
289
|
+
| ----------- | ---------------- | -------- | ---------------------------------------------- |
|
|
290
|
+
| `name` | `string` | Yes | File name with extension |
|
|
291
|
+
| `parentId` | `string \| null` | No | Parent folder ID (null or 'root' for root) |
|
|
292
|
+
| `accountId` | `string` | No | Storage account ID ('LOCAL' for local storage) |
|
|
293
|
+
| `enforce` | `boolean` | No | Bypass quota check (default: false) |
|
|
294
|
+
|
|
254
295
|
### Get Signed URL
|
|
255
296
|
|
|
256
297
|
```typescript
|
|
257
|
-
import { driveGetUrl } from
|
|
298
|
+
import { driveGetUrl } from "@muhgholy/next-drive/server";
|
|
258
299
|
|
|
259
300
|
// Default expiry (from config)
|
|
260
301
|
const url = driveGetUrl(fileId);
|
|
@@ -263,13 +304,13 @@ const url = driveGetUrl(fileId);
|
|
|
263
304
|
const url = driveGetUrl(fileId, { expiry: 7200 }); // 2 hours
|
|
264
305
|
|
|
265
306
|
// Specific date
|
|
266
|
-
const url = driveGetUrl(fileId, { expiry: new Date(
|
|
307
|
+
const url = driveGetUrl(fileId, { expiry: new Date("2026-12-31") });
|
|
267
308
|
```
|
|
268
309
|
|
|
269
310
|
### Read File Stream
|
|
270
311
|
|
|
271
312
|
```typescript
|
|
272
|
-
import { driveReadFile } from
|
|
313
|
+
import { driveReadFile } from "@muhgholy/next-drive/server";
|
|
273
314
|
|
|
274
315
|
// Using file ID
|
|
275
316
|
const { stream, mime, size } = await driveReadFile(fileId);
|
|
@@ -285,15 +326,15 @@ const { stream, mime, size } = await driveReadFile(drive);
|
|
|
285
326
|
For libraries requiring file paths (Sharp, FFmpeg, etc.):
|
|
286
327
|
|
|
287
328
|
```typescript
|
|
288
|
-
import { driveFilePath } from
|
|
329
|
+
import { driveFilePath } from "@muhgholy/next-drive/server";
|
|
289
330
|
|
|
290
331
|
const { path, mime, size, provider } = await driveFilePath(fileId);
|
|
291
332
|
|
|
292
333
|
// Use with Sharp
|
|
293
|
-
await sharp(path).resize(800, 600).toFile(
|
|
334
|
+
await sharp(path).resize(800, 600).toFile("output.jpg");
|
|
294
335
|
|
|
295
336
|
// Use with FFmpeg
|
|
296
|
-
await ffmpeg(path).format(
|
|
337
|
+
await ffmpeg(path).format("mp4").save("output.mp4");
|
|
297
338
|
```
|
|
298
339
|
|
|
299
340
|
> Google Drive files are automatically downloaded to local cache.
|
|
@@ -342,7 +383,7 @@ cors: {
|
|
|
342
383
|
**Client setup for CORS:**
|
|
343
384
|
|
|
344
385
|
```tsx
|
|
345
|
-
<DriveProvider apiEndpoint=
|
|
386
|
+
<DriveProvider apiEndpoint="https://api.example.com/drive" withCredentials={true}>
|
|
346
387
|
{children}
|
|
347
388
|
</DriveProvider>
|
|
348
389
|
```
|
|
@@ -2,9 +2,9 @@ import { __require } from './chunk-DGUM43GV.js';
|
|
|
2
2
|
import formidable from 'formidable';
|
|
3
3
|
import path3 from 'path';
|
|
4
4
|
import fs2 from 'fs';
|
|
5
|
-
import
|
|
5
|
+
import os2 from 'os';
|
|
6
6
|
import mongoose2, { Schema, isValidObjectId } from 'mongoose';
|
|
7
|
-
import
|
|
7
|
+
import crypto3 from 'crypto';
|
|
8
8
|
import sharp from 'sharp';
|
|
9
9
|
import { z } from 'zod';
|
|
10
10
|
import ffmpeg from 'fluent-ffmpeg';
|
|
@@ -138,7 +138,7 @@ var validateMimeType = (mime, allowedTypes) => {
|
|
|
138
138
|
});
|
|
139
139
|
};
|
|
140
140
|
var computeFileHash = (filePath) => new Promise((resolve, reject) => {
|
|
141
|
-
const hash =
|
|
141
|
+
const hash = crypto3.createHash("sha256");
|
|
142
142
|
const stream = fs2.createReadStream(filePath);
|
|
143
143
|
stream.on("data", (data) => hash.update(data));
|
|
144
144
|
stream.on("end", () => resolve(hash.digest("hex")));
|
|
@@ -252,7 +252,18 @@ var LocalStorageProvider = {
|
|
|
252
252
|
search: async (query, owner, accountId) => {
|
|
253
253
|
},
|
|
254
254
|
getQuota: async (owner, accountId, configuredQuotaInBytes) => {
|
|
255
|
-
const result = await drive_default.aggregate([
|
|
255
|
+
const result = await drive_default.aggregate([
|
|
256
|
+
{
|
|
257
|
+
$match: {
|
|
258
|
+
owner,
|
|
259
|
+
"information.type": "FILE",
|
|
260
|
+
trashedAt: null,
|
|
261
|
+
"provider.type": "LOCAL",
|
|
262
|
+
storageAccountId: accountId || null
|
|
263
|
+
}
|
|
264
|
+
},
|
|
265
|
+
{ $group: { _id: null, total: { $sum: "$information.sizeInBytes" } } }
|
|
266
|
+
]);
|
|
256
267
|
const usedInBytes = result[0]?.total || 0;
|
|
257
268
|
return { usedInBytes, quotaInBytes: configuredQuotaInBytes ?? 0 };
|
|
258
269
|
},
|
|
@@ -297,7 +308,7 @@ var LocalStorageProvider = {
|
|
|
297
308
|
return fs2.createReadStream(thumbPath);
|
|
298
309
|
},
|
|
299
310
|
createFolder: async (name, parentId, owner, accountId) => {
|
|
300
|
-
const
|
|
311
|
+
const getNextOrderValue2 = async (owner2) => {
|
|
301
312
|
const lastItem = await drive_default.findOne({ owner: owner2 }, {}, { sort: { order: -1 } });
|
|
302
313
|
return lastItem ? lastItem.order + 1 : 0;
|
|
303
314
|
};
|
|
@@ -305,7 +316,7 @@ var LocalStorageProvider = {
|
|
|
305
316
|
owner,
|
|
306
317
|
name,
|
|
307
318
|
parentId: parentId === "root" || !parentId ? null : parentId,
|
|
308
|
-
order: await
|
|
319
|
+
order: await getNextOrderValue2(owner),
|
|
309
320
|
provider: { type: "LOCAL" },
|
|
310
321
|
information: { type: "FOLDER" },
|
|
311
322
|
status: "READY"
|
|
@@ -815,6 +826,10 @@ var GoogleDriveProvider = {
|
|
|
815
826
|
}
|
|
816
827
|
}
|
|
817
828
|
};
|
|
829
|
+
var getNextOrderValue = async (owner) => {
|
|
830
|
+
const lastItem = await drive_default.findOne({ owner }, {}, { sort: { order: -1 } });
|
|
831
|
+
return lastItem ? lastItem.order + 1 : 0;
|
|
832
|
+
};
|
|
818
833
|
var driveGetUrl = (fileId, options) => {
|
|
819
834
|
const config = getDriveConfig();
|
|
820
835
|
if (!config.security.signedUrls?.enabled) {
|
|
@@ -829,7 +844,7 @@ var driveGetUrl = (fileId, options) => {
|
|
|
829
844
|
} else {
|
|
830
845
|
expiryTimestamp = Math.floor(Date.now() / 1e3) + expiresIn;
|
|
831
846
|
}
|
|
832
|
-
const signature =
|
|
847
|
+
const signature = crypto3.createHmac("sha256", secret).update(`${fileId}:${expiryTimestamp}`).digest("hex");
|
|
833
848
|
const token = Buffer.from(`${expiryTimestamp}:${signature}`).toString("base64url");
|
|
834
849
|
return `/api/drive?action=serve&id=${fileId}&token=${token}`;
|
|
835
850
|
};
|
|
@@ -931,6 +946,110 @@ var driveFilePath = async (file) => {
|
|
|
931
946
|
}
|
|
932
947
|
throw new Error(`Unsupported provider: ${providerType}`);
|
|
933
948
|
};
|
|
949
|
+
var driveUpload = async (source, key, options) => {
|
|
950
|
+
const config = getDriveConfig();
|
|
951
|
+
let provider = LocalStorageProvider;
|
|
952
|
+
const accountId = options.accountId;
|
|
953
|
+
if (accountId && accountId !== "LOCAL") {
|
|
954
|
+
const account = await drive_default.db.model("StorageAccount").findOne({ _id: accountId, owner: key });
|
|
955
|
+
if (!account) {
|
|
956
|
+
throw new Error("Invalid Storage Account");
|
|
957
|
+
}
|
|
958
|
+
if (account.metadata.provider === "GOOGLE") {
|
|
959
|
+
provider = GoogleDriveProvider;
|
|
960
|
+
}
|
|
961
|
+
}
|
|
962
|
+
let tempFilePath = null;
|
|
963
|
+
let sourceFilePath;
|
|
964
|
+
let fileSize;
|
|
965
|
+
if (typeof source === "string") {
|
|
966
|
+
if (!fs2.existsSync(source)) {
|
|
967
|
+
throw new Error(`Source file not found: ${source}`);
|
|
968
|
+
}
|
|
969
|
+
sourceFilePath = source;
|
|
970
|
+
const stats = fs2.statSync(source);
|
|
971
|
+
fileSize = stats.size;
|
|
972
|
+
} else {
|
|
973
|
+
const tempDir = path3.join(os2.tmpdir(), "next-drive-uploads");
|
|
974
|
+
if (!fs2.existsSync(tempDir)) {
|
|
975
|
+
fs2.mkdirSync(tempDir, { recursive: true });
|
|
976
|
+
}
|
|
977
|
+
tempFilePath = path3.join(tempDir, `upload-${crypto3.randomUUID()}.tmp`);
|
|
978
|
+
const writeStream = fs2.createWriteStream(tempFilePath);
|
|
979
|
+
await new Promise((resolve, reject) => {
|
|
980
|
+
source.pipe(writeStream);
|
|
981
|
+
writeStream.on("finish", resolve);
|
|
982
|
+
writeStream.on("error", reject);
|
|
983
|
+
source.on("error", reject);
|
|
984
|
+
});
|
|
985
|
+
sourceFilePath = tempFilePath;
|
|
986
|
+
const stats = fs2.statSync(tempFilePath);
|
|
987
|
+
fileSize = stats.size;
|
|
988
|
+
}
|
|
989
|
+
try {
|
|
990
|
+
const ext = path3.extname(options.name).toLowerCase();
|
|
991
|
+
const mimeTypes = {
|
|
992
|
+
".jpg": "image/jpeg",
|
|
993
|
+
".jpeg": "image/jpeg",
|
|
994
|
+
".png": "image/png",
|
|
995
|
+
".gif": "image/gif",
|
|
996
|
+
".webp": "image/webp",
|
|
997
|
+
".svg": "image/svg+xml",
|
|
998
|
+
".mp4": "video/mp4",
|
|
999
|
+
".mov": "video/quicktime",
|
|
1000
|
+
".avi": "video/x-msvideo",
|
|
1001
|
+
".pdf": "application/pdf",
|
|
1002
|
+
".doc": "application/msword",
|
|
1003
|
+
".docx": "application/vnd.openxmlformats-officedocument.wordprocessingml.document",
|
|
1004
|
+
".xls": "application/vnd.ms-excel",
|
|
1005
|
+
".xlsx": "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
|
|
1006
|
+
".txt": "text/plain",
|
|
1007
|
+
".json": "application/json",
|
|
1008
|
+
".zip": "application/zip"
|
|
1009
|
+
};
|
|
1010
|
+
const mimeType = mimeTypes[ext] || "application/octet-stream";
|
|
1011
|
+
if (!validateMimeType(mimeType, config.security.allowedMimeTypes)) {
|
|
1012
|
+
throw new Error(`File type ${mimeType} not allowed`);
|
|
1013
|
+
}
|
|
1014
|
+
if (fileSize > config.security.maxUploadSizeInBytes) {
|
|
1015
|
+
throw new Error(`File size ${fileSize} exceeds maximum allowed size ${config.security.maxUploadSizeInBytes}`);
|
|
1016
|
+
}
|
|
1017
|
+
if (!options.enforce) {
|
|
1018
|
+
const quota = await provider.getQuota(key, accountId, void 0);
|
|
1019
|
+
if (quota.usedInBytes + fileSize > quota.quotaInBytes) {
|
|
1020
|
+
throw new Error("Storage quota exceeded");
|
|
1021
|
+
}
|
|
1022
|
+
}
|
|
1023
|
+
const drive = new drive_default({
|
|
1024
|
+
owner: key,
|
|
1025
|
+
storageAccountId: accountId || null,
|
|
1026
|
+
provider: { type: provider.name },
|
|
1027
|
+
name: options.name,
|
|
1028
|
+
parentId: options.parentId === "root" || !options.parentId ? null : options.parentId,
|
|
1029
|
+
order: await getNextOrderValue(key),
|
|
1030
|
+
information: { type: "FILE", sizeInBytes: fileSize, mime: mimeType, path: "" },
|
|
1031
|
+
status: "UPLOADING"
|
|
1032
|
+
});
|
|
1033
|
+
if (provider.name === "LOCAL" && drive.information.type === "FILE") {
|
|
1034
|
+
let sanitizedExt = path3.extname(options.name) || ".bin";
|
|
1035
|
+
sanitizedExt = sanitizedExt.replace(/[^a-zA-Z0-9.]/g, "").slice(0, 11);
|
|
1036
|
+
if (!sanitizedExt.startsWith(".")) sanitizedExt = ".bin";
|
|
1037
|
+
drive.information.path = path3.join(String(drive._id), `data${sanitizedExt}`);
|
|
1038
|
+
}
|
|
1039
|
+
await drive.save();
|
|
1040
|
+
try {
|
|
1041
|
+
const item = await provider.uploadFile(drive, sourceFilePath, accountId);
|
|
1042
|
+
return item;
|
|
1043
|
+
} catch (err) {
|
|
1044
|
+
await drive_default.deleteOne({ _id: drive._id });
|
|
1045
|
+
throw err;
|
|
1046
|
+
}
|
|
1047
|
+
} finally {
|
|
1048
|
+
if (tempFilePath && fs2.existsSync(tempFilePath)) {
|
|
1049
|
+
fs2.rmSync(tempFilePath, { force: true });
|
|
1050
|
+
}
|
|
1051
|
+
}
|
|
1052
|
+
};
|
|
934
1053
|
|
|
935
1054
|
// src/server/index.ts
|
|
936
1055
|
var getProvider = async (req, owner) => {
|
|
@@ -1192,7 +1311,7 @@ var driveAPIHandler = async (req, res) => {
|
|
|
1192
1311
|
// ** 3. UPLOAD **
|
|
1193
1312
|
case "upload": {
|
|
1194
1313
|
if (req.method !== "POST") return res.status(405).json({ status: 405, message: "Only POST allowed" });
|
|
1195
|
-
const systemTmpDir = path3.join(
|
|
1314
|
+
const systemTmpDir = path3.join(os2.tmpdir(), "next-drive-uploads");
|
|
1196
1315
|
if (!fs2.existsSync(systemTmpDir)) fs2.mkdirSync(systemTmpDir, { recursive: true });
|
|
1197
1316
|
const form = formidable({
|
|
1198
1317
|
multiples: false,
|
|
@@ -1228,7 +1347,7 @@ var driveAPIHandler = async (req, res) => {
|
|
|
1228
1347
|
}
|
|
1229
1348
|
const { chunkIndex, totalChunks, driveId, fileName, fileSize: fileSizeInBytes, fileType, folderId } = uploadData.data;
|
|
1230
1349
|
let currentUploadId = driveId;
|
|
1231
|
-
const tempBaseDir = path3.join(
|
|
1350
|
+
const tempBaseDir = path3.join(os2.tmpdir(), "next-drive-uploads");
|
|
1232
1351
|
if (!currentUploadId) {
|
|
1233
1352
|
if (chunkIndex !== 0) return res.status(400).json({ message: "Missing upload ID for non-zero chunk" });
|
|
1234
1353
|
if (fileType && !validateMimeType(fileType, config.security.allowedMimeTypes)) {
|
|
@@ -1357,7 +1476,7 @@ var driveAPIHandler = async (req, res) => {
|
|
|
1357
1476
|
const cancelData = cancelQuerySchema.safeParse(req.query);
|
|
1358
1477
|
if (!cancelData.success) return res.status(400).json({ status: 400, message: "Invalid ID" });
|
|
1359
1478
|
const { id } = cancelData.data;
|
|
1360
|
-
const tempUploadDir = path3.join(
|
|
1479
|
+
const tempUploadDir = path3.join(os2.tmpdir(), "next-drive-uploads", id);
|
|
1361
1480
|
if (fs2.existsSync(tempUploadDir)) {
|
|
1362
1481
|
try {
|
|
1363
1482
|
fs2.rmSync(tempUploadDir, { recursive: true, force: true });
|
|
@@ -1538,6 +1657,6 @@ var driveAPIHandler = async (req, res) => {
|
|
|
1538
1657
|
}
|
|
1539
1658
|
};
|
|
1540
1659
|
|
|
1541
|
-
export { driveAPIHandler, driveConfiguration, driveFilePath, driveFileSchemaZod, driveGetUrl, driveReadFile, getDriveConfig, getDriveInformation };
|
|
1542
|
-
//# sourceMappingURL=chunk-
|
|
1543
|
-
//# sourceMappingURL=chunk-
|
|
1660
|
+
export { driveAPIHandler, driveConfiguration, driveFilePath, driveFileSchemaZod, driveGetUrl, driveReadFile, driveUpload, getDriveConfig, getDriveInformation };
|
|
1661
|
+
//# sourceMappingURL=chunk-F5ZVJGYN.js.map
|
|
1662
|
+
//# sourceMappingURL=chunk-F5ZVJGYN.js.map
|