@muhgholy/next-drive 2.2.1 → 2.2.2
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 +222 -346
- package/dist/client/index.d.ts +0 -1
- package/dist/client/index.d.ts.map +1 -1
- package/dist/client/index.js +21 -1
- package/dist/client/index.js.map +1 -1
- package/dist/client/styles.css +4 -0
- package/package.json +5 -1
- package/dist/client/index.css +0 -59
- package/dist/client/index.css.map +0 -1
package/README.md
CHANGED
|
@@ -1,16 +1,18 @@
|
|
|
1
1
|
# @muhgholy/next-drive
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
File storage and management for Next.js and Express apps. Includes a responsive UI, search, trash system, and secure file handling.
|
|
4
4
|
|
|
5
5
|
## Features
|
|
6
6
|
|
|
7
|
-
- **File Management
|
|
8
|
-
- **
|
|
9
|
-
- **Trash System
|
|
10
|
-
- **Responsive UI
|
|
11
|
-
- **Video
|
|
12
|
-
- **Security
|
|
13
|
-
- **View Modes
|
|
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
|
+
|
|
15
|
+
---
|
|
14
16
|
|
|
15
17
|
## Installation
|
|
16
18
|
|
|
@@ -18,59 +20,60 @@ Robust file storage and management solution for Next.js and Express applications
|
|
|
18
20
|
npm install @muhgholy/next-drive
|
|
19
21
|
```
|
|
20
22
|
|
|
21
|
-
|
|
23
|
+
### Requirements
|
|
24
|
+
|
|
25
|
+
| Dependency | Version |
|
|
26
|
+
| ------------ | ------- |
|
|
27
|
+
| Next.js | >= 14 |
|
|
28
|
+
| React | >= 18 |
|
|
29
|
+
| Mongoose | >= 7 |
|
|
30
|
+
| Tailwind CSS | >= 3 |
|
|
22
31
|
|
|
23
|
-
|
|
24
|
-
- React >= 18
|
|
25
|
-
- Mongoose >= 7
|
|
26
|
-
- Tailwind CSS >= 3
|
|
32
|
+
**FFmpeg** (for video thumbnails):
|
|
27
33
|
|
|
28
|
-
|
|
34
|
+
```bash
|
|
35
|
+
# macOS
|
|
36
|
+
brew install ffmpeg
|
|
29
37
|
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
38
|
+
# Ubuntu
|
|
39
|
+
sudo apt install ffmpeg
|
|
40
|
+
|
|
41
|
+
# Windows
|
|
42
|
+
# Download from https://ffmpeg.org and add to PATH
|
|
43
|
+
```
|
|
34
44
|
|
|
35
|
-
### Tailwind
|
|
45
|
+
### Tailwind Setup
|
|
36
46
|
|
|
37
|
-
|
|
47
|
+
Add the package to your Tailwind content config:
|
|
38
48
|
|
|
39
49
|
```js
|
|
40
50
|
// tailwind.config.js
|
|
41
51
|
export default {
|
|
42
|
-
content: [
|
|
43
|
-
'./app/**/*.{js,ts,jsx,tsx,mdx}',
|
|
44
|
-
'./pages/**/*.{js,ts,jsx,tsx,mdx}',
|
|
45
|
-
'./components/**/*.{js,ts,jsx,tsx,mdx}',
|
|
46
|
-
// Add the next-drive package
|
|
47
|
-
'./node_modules/@muhgholy/next-drive/dist/**/*.{js,mjs}',
|
|
48
|
-
],
|
|
49
|
-
theme: {
|
|
50
|
-
extend: {},
|
|
51
|
-
},
|
|
52
|
-
plugins: [],
|
|
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
|
|
|
56
|
-
>
|
|
56
|
+
> CSS is auto-injected when importing from `@muhgholy/next-drive/client`.
|
|
57
|
+
|
|
58
|
+
---
|
|
57
59
|
|
|
58
60
|
## Quick Start
|
|
59
61
|
|
|
60
|
-
### 1.
|
|
62
|
+
### 1. Server Configuration
|
|
61
63
|
|
|
62
|
-
Create
|
|
64
|
+
Create `lib/drive.ts` to configure storage, security, and authentication:
|
|
63
65
|
|
|
64
66
|
```typescript
|
|
65
67
|
// lib/drive.ts
|
|
66
68
|
import { driveConfiguration } from '@muhgholy/next-drive/server';
|
|
67
69
|
import type { TDriveConfigInformation } from '@muhgholy/next-drive/server';
|
|
68
70
|
|
|
69
|
-
|
|
71
|
+
driveConfiguration({
|
|
70
72
|
database: 'MONGOOSE',
|
|
73
|
+
apiUrl: '/api/drive',
|
|
71
74
|
storage: { path: '/var/data/drive' },
|
|
72
75
|
security: {
|
|
73
|
-
|
|
76
|
+
maxUploadSizeInBytes: 50 * 1024 * 1024, // 50MB
|
|
74
77
|
allowedMimeTypes: ['image/*', 'video/*', 'application/pdf'],
|
|
75
78
|
signedUrls: {
|
|
76
79
|
enabled: true,
|
|
@@ -78,36 +81,20 @@ export const drive = driveConfiguration({
|
|
|
78
81
|
expiresIn: 3600, // 1 hour
|
|
79
82
|
},
|
|
80
83
|
},
|
|
81
|
-
image: {
|
|
82
|
-
formats: ['webp', 'jpeg', 'png'],
|
|
83
|
-
qualities: ['ultralow', 'low', 'medium', 'high', 'normal'],
|
|
84
|
-
},
|
|
85
|
-
// Optional: Enable CORS for cross-origin requests
|
|
86
|
-
cors: {
|
|
87
|
-
enabled: true,
|
|
88
|
-
origins: ['https://example.com', 'https://app.example.com'],
|
|
89
|
-
credentials: true,
|
|
90
|
-
},
|
|
91
84
|
information: async (req): Promise<TDriveConfigInformation> => {
|
|
92
|
-
// Implement your auth verification here
|
|
93
85
|
const auth = await verifyAuth(req);
|
|
94
86
|
if (!auth) throw new Error('Unauthenticated');
|
|
95
87
|
return {
|
|
96
88
|
key: { userId: auth.userId },
|
|
97
|
-
storage: { quotaInBytes: 1024 * 1024 * 1024 }, // 1GB
|
|
89
|
+
storage: { quotaInBytes: 1024 * 1024 * 1024 }, // 1GB
|
|
98
90
|
};
|
|
99
91
|
},
|
|
100
92
|
});
|
|
101
93
|
```
|
|
102
94
|
|
|
103
|
-
### 2.
|
|
104
|
-
|
|
105
|
-
Set up the API route handler that `next-drive` will use to communicate with the client.
|
|
106
|
-
|
|
107
|
-
**Important:**
|
|
95
|
+
### 2. API Route (Pages Router)
|
|
108
96
|
|
|
109
|
-
|
|
110
|
-
- **You MUST disable Next.js body parser** for uploads to work properly
|
|
97
|
+
> ⚠️ **Important**: Must be in `pages/` folder with body parser disabled.
|
|
111
98
|
|
|
112
99
|
```typescript
|
|
113
100
|
// pages/api/drive.ts
|
|
@@ -116,67 +103,82 @@ import { driveAPIHandler } from '@muhgholy/next-drive/server';
|
|
|
116
103
|
import type { NextApiRequest, NextApiResponse } from 'next';
|
|
117
104
|
|
|
118
105
|
export default async function handler(req: NextApiRequest, res: NextApiResponse) {
|
|
119
|
-
//
|
|
120
|
-
if (!req.body) req.body = {};
|
|
121
|
-
|
|
106
|
+
// Parse JSON body manually (body parser is disabled)
|
|
122
107
|
if (req.headers['content-type']?.includes('application/json')) {
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
});
|
|
130
|
-
|
|
131
|
-
if (buffer.length > 0) {
|
|
132
|
-
req.body = JSON.parse(buffer.toString());
|
|
133
|
-
}
|
|
134
|
-
} catch (e) {
|
|
135
|
-
console.error('Failed to parse JSON body', e);
|
|
136
|
-
}
|
|
108
|
+
const chunks: Buffer[] = [];
|
|
109
|
+
for await (const chunk of req) chunks.push(chunk);
|
|
110
|
+
const buffer = Buffer.concat(chunks);
|
|
111
|
+
req.body = buffer.length > 0 ? JSON.parse(buffer.toString()) : {};
|
|
112
|
+
} else {
|
|
113
|
+
req.body = req.body || {};
|
|
137
114
|
}
|
|
138
115
|
|
|
139
116
|
return driveAPIHandler(req, res);
|
|
140
117
|
}
|
|
141
118
|
|
|
142
|
-
// ⚠️ CRITICAL: Disable body parser for file uploads
|
|
143
119
|
export const config = {
|
|
144
|
-
api: {
|
|
145
|
-
bodyParser: false,
|
|
146
|
-
},
|
|
120
|
+
api: { bodyParser: false },
|
|
147
121
|
};
|
|
148
122
|
```
|
|
149
123
|
|
|
150
|
-
###
|
|
124
|
+
### 3. Client Provider
|
|
125
|
+
|
|
126
|
+
Wrap your app with `DriveProvider`:
|
|
127
|
+
|
|
128
|
+
```tsx
|
|
129
|
+
// app/layout.tsx
|
|
130
|
+
import { DriveProvider } from '@muhgholy/next-drive/client';
|
|
131
|
+
|
|
132
|
+
export default function RootLayout({ children }) {
|
|
133
|
+
return <DriveProvider apiEndpoint='/api/drive'>{children}</DriveProvider>;
|
|
134
|
+
}
|
|
135
|
+
```
|
|
136
|
+
|
|
137
|
+
### 4. UI Components
|
|
138
|
+
|
|
139
|
+
**File Explorer:**
|
|
140
|
+
|
|
141
|
+
```tsx
|
|
142
|
+
import { DriveExplorer } from '@muhgholy/next-drive/client';
|
|
143
|
+
|
|
144
|
+
export default function DrivePage() {
|
|
145
|
+
return <DriveExplorer />;
|
|
146
|
+
}
|
|
147
|
+
```
|
|
148
|
+
|
|
149
|
+
**File Picker (for forms):**
|
|
150
|
+
|
|
151
|
+
```tsx
|
|
152
|
+
import { useState } from 'react';
|
|
153
|
+
import { DriveFileChooser } from '@muhgholy/next-drive/client';
|
|
154
|
+
import type { TDriveFile } from '@muhgholy/next-drive/client';
|
|
155
|
+
|
|
156
|
+
function MyForm() {
|
|
157
|
+
const [file, setFile] = useState<TDriveFile | null>(null);
|
|
158
|
+
return <DriveFileChooser value={file} onChange={setFile} accept='image/*' />;
|
|
159
|
+
}
|
|
160
|
+
```
|
|
161
|
+
|
|
162
|
+
---
|
|
163
|
+
|
|
164
|
+
## Express Integration
|
|
151
165
|
|
|
152
|
-
|
|
166
|
+
Use the Express adapter instead of Next.js API routes:
|
|
153
167
|
|
|
154
168
|
```typescript
|
|
155
169
|
// lib/drive.ts
|
|
156
170
|
import { driveConfigurationExpress } from '@muhgholy/next-drive/server/express';
|
|
157
171
|
import type { TDriveConfigInformation } from '@muhgholy/next-drive/server/express';
|
|
158
172
|
|
|
159
|
-
|
|
173
|
+
driveConfigurationExpress({
|
|
160
174
|
database: 'MONGOOSE',
|
|
175
|
+
apiUrl: '/api/drive',
|
|
161
176
|
storage: { path: '/var/data/drive' },
|
|
162
177
|
security: {
|
|
163
|
-
maxUploadSizeInBytes: 50 * 1024 * 1024,
|
|
178
|
+
maxUploadSizeInBytes: 50 * 1024 * 1024,
|
|
164
179
|
allowedMimeTypes: ['image/*', 'video/*', 'application/pdf'],
|
|
165
|
-
signedUrls: {
|
|
166
|
-
enabled: true,
|
|
167
|
-
secret: process.env.DRIVE_SECRET!,
|
|
168
|
-
expiresIn: 3600,
|
|
169
|
-
},
|
|
170
180
|
},
|
|
171
|
-
// Optional: Enable CORS for cross-origin requests
|
|
172
|
-
cors: {
|
|
173
|
-
enabled: true,
|
|
174
|
-
origins: ['https://example.com'],
|
|
175
|
-
credentials: true,
|
|
176
|
-
},
|
|
177
|
-
apiUrl: '/api/drive',
|
|
178
181
|
information: async (req): Promise<TDriveConfigInformation> => {
|
|
179
|
-
// req is Express Request type
|
|
180
182
|
const auth = await verifyAuth(req);
|
|
181
183
|
if (!auth) throw new Error('Unauthenticated');
|
|
182
184
|
return {
|
|
@@ -189,168 +191,82 @@ export const drive = driveConfigurationExpress({
|
|
|
189
191
|
|
|
190
192
|
```typescript
|
|
191
193
|
// routes/drive.ts
|
|
192
|
-
import './lib/drive';
|
|
194
|
+
import './lib/drive';
|
|
193
195
|
import express from 'express';
|
|
194
196
|
import { driveAPIHandlerExpress } from '@muhgholy/next-drive/server/express';
|
|
195
197
|
|
|
196
198
|
const router = express.Router();
|
|
197
|
-
|
|
198
|
-
// Handle all drive API requests
|
|
199
199
|
router.all('/drive', driveAPIHandlerExpress);
|
|
200
200
|
|
|
201
201
|
export default router;
|
|
202
202
|
```
|
|
203
203
|
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
- Do NOT use `express.json()` middleware on the drive route (file uploads need raw body)
|
|
207
|
-
- The handler supports all HTTP methods (GET, POST, PATCH, DELETE)
|
|
208
|
-
|
|
209
|
-
### 3. Add Provider
|
|
210
|
-
|
|
211
|
-
Wrap your application or the specific route with `DriveProvider`.
|
|
212
|
-
|
|
213
|
-
```typescript
|
|
214
|
-
// app/layout.tsx
|
|
215
|
-
import { DriveProvider } from "@muhgholy/next-drive/client";
|
|
216
|
-
|
|
217
|
-
export default function RootLayout({ children }) {
|
|
218
|
-
return <DriveProvider apiEndpoint="/api/drive">{children}</DriveProvider>;
|
|
219
|
-
}
|
|
220
|
-
```
|
|
221
|
-
|
|
222
|
-
**Cross-Origin Setup:**
|
|
223
|
-
|
|
224
|
-
When your client runs on a different domain than the API, enable credentials:
|
|
225
|
-
|
|
226
|
-
```typescript
|
|
227
|
-
// Enable cookies/auth headers for cross-origin requests
|
|
228
|
-
<DriveProvider
|
|
229
|
-
apiEndpoint="https://api.example.com/drive"
|
|
230
|
-
withCredentials={true}
|
|
231
|
-
>
|
|
232
|
-
{children}
|
|
233
|
-
</DriveProvider>
|
|
234
|
-
```
|
|
235
|
-
|
|
236
|
-
> **Note**: Requires matching CORS configuration on the server with `credentials: true`.
|
|
237
|
-
|
|
238
|
-
### 4. Implement UI Components
|
|
239
|
-
|
|
240
|
-
You can use the built-in `DriveExplorer` for a full file manager experience or `DriveFileChooser` for form inputs.
|
|
241
|
-
|
|
242
|
-
**Full File Explorer:**
|
|
243
|
-
|
|
244
|
-
```typescript
|
|
245
|
-
import { DriveExplorer } from "@muhgholy/next-drive/client";
|
|
246
|
-
|
|
247
|
-
export default function DrivePage() {
|
|
248
|
-
return <DriveExplorer />;
|
|
249
|
-
}
|
|
250
|
-
```
|
|
251
|
-
|
|
252
|
-
**File Picker:**
|
|
253
|
-
|
|
254
|
-
```typescript
|
|
255
|
-
import { DriveFileChooser } from "@muhgholy/next-drive/client";
|
|
256
|
-
import type { TDriveFile } from "@muhgholy/next-drive/client";
|
|
204
|
+
> ⚠️ Don't use `express.json()` middleware on this route.
|
|
257
205
|
|
|
258
|
-
|
|
259
|
-
const [file, setFile] = useState<TDriveFile | null>(null);
|
|
206
|
+
---
|
|
260
207
|
|
|
261
|
-
|
|
262
|
-
}
|
|
263
|
-
```
|
|
208
|
+
## Zod Validation
|
|
264
209
|
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
You can use the exported `driveFileSchemaZod` to validate file data in your forms or API routes.
|
|
210
|
+
Validate file data in forms or API routes:
|
|
268
211
|
|
|
269
212
|
```typescript
|
|
270
213
|
import { z } from 'zod';
|
|
271
214
|
import { driveFileSchemaZod } from '@muhgholy/next-drive/schemas';
|
|
272
215
|
|
|
273
|
-
|
|
274
|
-
const myFormSchema = z.object({
|
|
216
|
+
const formSchema = z.object({
|
|
275
217
|
asset: driveFileSchemaZod,
|
|
276
218
|
title: z.string(),
|
|
277
|
-
description: z.string().optional(),
|
|
278
219
|
});
|
|
279
|
-
|
|
280
|
-
type MyFormData = z.infer<typeof myFormSchema>;
|
|
281
220
|
```
|
|
282
221
|
|
|
283
|
-
>
|
|
222
|
+
> Schema also available from `/client` and `/server` exports.
|
|
284
223
|
|
|
285
|
-
|
|
286
|
-
const myFormSchema = z.object({
|
|
224
|
+
---
|
|
287
225
|
|
|
288
|
-
|
|
226
|
+
## Client-Side File URLs
|
|
289
227
|
|
|
290
|
-
|
|
228
|
+
Generate URLs for displaying files:
|
|
291
229
|
|
|
292
|
-
|
|
230
|
+
```tsx
|
|
231
|
+
import { useDrive } from '@muhgholy/next-drive/client';
|
|
232
|
+
import type { TDriveFile } from '@muhgholy/next-drive/client';
|
|
293
233
|
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
```typescript
|
|
297
|
-
import { useDrive } from "@muhgholy/next-drive/client";
|
|
298
|
-
import type { TDriveFile } from "@muhgholy/next-drive/client";
|
|
299
|
-
|
|
300
|
-
function MyComponent() {
|
|
301
|
-
const { createUrl } = useDrive();
|
|
234
|
+
function MyComponent({ driveFile }: { driveFile: TDriveFile }) {
|
|
235
|
+
const { createUrl, createSrcSet } = useDrive();
|
|
302
236
|
|
|
303
|
-
// Basic URL
|
|
237
|
+
// Basic URL
|
|
304
238
|
const url = createUrl(driveFile);
|
|
305
|
-
// Returns: /api/drive?action=serve&id={fileId}
|
|
306
|
-
|
|
307
|
-
// With image quality and format
|
|
308
|
-
const url = createUrl(driveFile, {
|
|
309
|
-
quality: "medium",
|
|
310
|
-
format: "webp",
|
|
311
|
-
});
|
|
312
|
-
// Returns: /api/drive?action=serve&id={fileId}&q=medium&format=webp
|
|
313
|
-
|
|
314
|
-
// Use in Next.js Image component
|
|
315
|
-
return <Image src={createUrl(driveFile)} alt={driveFile.file.name} />;
|
|
316
|
-
}
|
|
317
|
-
````
|
|
318
239
|
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
```typescript
|
|
322
|
-
import { useDrive } from "@muhgholy/next-drive/client";
|
|
323
|
-
|
|
324
|
-
function ResponsiveImage({ driveFile }: { driveFile: TDriveFile }) {
|
|
325
|
-
const { createUrl, createSrcSet } = useDrive();
|
|
240
|
+
// With quality and format
|
|
241
|
+
const optimizedUrl = createUrl(driveFile, { quality: 'medium', format: 'webp' });
|
|
326
242
|
|
|
327
|
-
//
|
|
328
|
-
const { srcSet, sizes } = createSrcSet(driveFile,
|
|
243
|
+
// Responsive srcSet for images
|
|
244
|
+
const { srcSet, sizes } = createSrcSet(driveFile, 'webp');
|
|
329
245
|
|
|
330
|
-
|
|
331
|
-
return <img src={createUrl(driveFile, { quality: "medium" })} srcSet={srcSet} sizes={sizes} alt={driveFile.file.name} />;
|
|
246
|
+
return <img src={optimizedUrl} srcSet={srcSet} sizes={sizes} alt={driveFile.file.name} />;
|
|
332
247
|
}
|
|
333
248
|
```
|
|
334
249
|
|
|
335
|
-
|
|
250
|
+
---
|
|
336
251
|
|
|
337
|
-
|
|
252
|
+
## Server-Side File Access
|
|
253
|
+
|
|
254
|
+
### Get Signed URL
|
|
338
255
|
|
|
339
256
|
```typescript
|
|
340
257
|
import { driveGetUrl } from '@muhgholy/next-drive/server';
|
|
341
258
|
|
|
342
|
-
//
|
|
259
|
+
// Default expiry (from config)
|
|
343
260
|
const url = driveGetUrl(fileId);
|
|
344
|
-
// Returns: /api/drive?action=serve&id={fileId}&token={signedToken}
|
|
345
261
|
|
|
346
|
-
//
|
|
262
|
+
// Custom expiry in seconds
|
|
347
263
|
const url = driveGetUrl(fileId, { expiry: 7200 }); // 2 hours
|
|
348
264
|
|
|
349
|
-
//
|
|
350
|
-
const url = driveGetUrl(fileId, { expiry: new Date('
|
|
265
|
+
// Specific date
|
|
266
|
+
const url = driveGetUrl(fileId, { expiry: new Date('2026-12-31') });
|
|
351
267
|
```
|
|
352
268
|
|
|
353
|
-
|
|
269
|
+
### Read File Stream
|
|
354
270
|
|
|
355
271
|
```typescript
|
|
356
272
|
import { driveReadFile } from '@muhgholy/next-drive/server';
|
|
@@ -362,191 +278,151 @@ stream.pipe(response);
|
|
|
362
278
|
// Using database document
|
|
363
279
|
const drive = await Drive.findById(fileId);
|
|
364
280
|
const { stream, mime, size } = await driveReadFile(drive);
|
|
365
|
-
|
|
366
|
-
// Example: Send file via email
|
|
367
|
-
const { stream } = await driveReadFile(fileId);
|
|
368
|
-
await sendEmail({
|
|
369
|
-
attachments: [{ filename: 'report.pdf', content: stream }],
|
|
370
|
-
});
|
|
371
|
-
|
|
372
|
-
// Example: Process file contents
|
|
373
|
-
const { stream } = await driveReadFile(fileId);
|
|
374
|
-
const chunks = [];
|
|
375
|
-
for await (const chunk of stream) {
|
|
376
|
-
chunks.push(chunk);
|
|
377
|
-
}
|
|
378
|
-
const buffer = Buffer.concat(chunks);
|
|
379
281
|
```
|
|
380
282
|
|
|
381
|
-
|
|
283
|
+
### Get Local File Path
|
|
382
284
|
|
|
383
|
-
For
|
|
285
|
+
For libraries requiring file paths (Sharp, FFmpeg, etc.):
|
|
384
286
|
|
|
385
287
|
```typescript
|
|
386
288
|
import { driveFilePath } from '@muhgholy/next-drive/server';
|
|
387
|
-
import fs from 'fs';
|
|
388
289
|
|
|
389
|
-
// Get local path (downloads Google Drive files automatically)
|
|
390
290
|
const { path, mime, size, provider } = await driveFilePath(fileId);
|
|
391
291
|
|
|
392
|
-
// Use with
|
|
393
|
-
const buffer = fs.readFileSync(path);
|
|
394
|
-
|
|
395
|
-
// Use with libraries requiring file paths
|
|
292
|
+
// Use with Sharp
|
|
396
293
|
await sharp(path).resize(800, 600).toFile('output.jpg');
|
|
397
|
-
await ffmpeg(path).format('mp4').save('output.mp4');
|
|
398
294
|
|
|
399
|
-
//
|
|
400
|
-
|
|
295
|
+
// Use with FFmpeg
|
|
296
|
+
await ffmpeg(path).format('mp4').save('output.mp4');
|
|
401
297
|
```
|
|
402
298
|
|
|
403
|
-
|
|
299
|
+
> Google Drive files are automatically downloaded to local cache.
|
|
404
300
|
|
|
405
|
-
|
|
406
|
-
- **Trash Management**: "Delete" moves items to Trash. From Trash, you can "Restore" items or "Delete Forever". A dedicated "Empty Trash" button is available to clear all deleted items.
|
|
301
|
+
---
|
|
407
302
|
|
|
408
|
-
|
|
303
|
+
## Configuration Options
|
|
409
304
|
|
|
410
|
-
|
|
305
|
+
### Security
|
|
411
306
|
|
|
412
307
|
```typescript
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
308
|
+
security: {
|
|
309
|
+
maxUploadSizeInBytes: 50 * 1024 * 1024, // 50MB
|
|
310
|
+
allowedMimeTypes: ['image/*', 'video/*', 'application/pdf'],
|
|
311
|
+
signedUrls: {
|
|
417
312
|
enabled: true,
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
allowedHeaders: ['Content-Type', 'Authorization', 'X-Drive-Account'], // default
|
|
421
|
-
exposedHeaders: ['Content-Length', 'Content-Type', 'Content-Disposition'], // default
|
|
422
|
-
credentials: true, // Allow cookies/auth headers
|
|
423
|
-
maxAge: 86400, // Preflight cache duration in seconds (default: 24 hours)
|
|
313
|
+
secret: process.env.DRIVE_SECRET!,
|
|
314
|
+
expiresIn: 3600, // seconds
|
|
424
315
|
},
|
|
425
|
-
}
|
|
316
|
+
trash: { retentionDays: 30 },
|
|
317
|
+
}
|
|
426
318
|
```
|
|
427
319
|
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
| Option | Type | Default | Description |
|
|
431
|
-
| ---------------- | -------------------- | ----------------------------------------------------------- | ------------------------------------------- |
|
|
432
|
-
| `enabled` | `boolean` | `false` | Enable/disable CORS headers |
|
|
433
|
-
| `origins` | `string \| string[]` | `'*'` | Allowed origins (use array for multiple) |
|
|
434
|
-
| `methods` | `string[]` | `['GET', 'POST', 'PUT', 'DELETE', 'OPTIONS']` | Allowed HTTP methods |
|
|
435
|
-
| `allowedHeaders` | `string[]` | `['Content-Type', 'Authorization', 'X-Drive-Account']` | Headers clients can send |
|
|
436
|
-
| `exposedHeaders` | `string[]` | `['Content-Length', 'Content-Type', 'Content-Disposition']` | Headers exposed to client |
|
|
437
|
-
| `credentials` | `boolean` | `false` | Allow credentials (cookies, auth headers) |
|
|
438
|
-
| `maxAge` | `number` | `86400` | Preflight response cache duration (seconds) |
|
|
439
|
-
|
|
440
|
-
> **Note**: When `credentials` is `true`, `origins` cannot be `'*'`. You must specify explicit origins.
|
|
320
|
+
### Image Processing
|
|
441
321
|
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
1. Go to [Google Cloud Console](https://console.cloud.google.com/)
|
|
449
|
-
2. Create a new project or select an existing one
|
|
450
|
-
3. Enable the **Google Drive API** from the API Library
|
|
451
|
-
4. Go to **Credentials** → **Create Credentials** → **OAuth 2.0 Client ID**
|
|
452
|
-
5. Select **Web application** as the application type
|
|
453
|
-
6. Add your redirect URI (e.g., `http://localhost:3000/api/auth/google/callback`)
|
|
454
|
-
7. Copy the **Client ID** and **Client Secret**
|
|
322
|
+
```typescript
|
|
323
|
+
image: {
|
|
324
|
+
formats: ['webp', 'jpeg', 'png'],
|
|
325
|
+
qualities: ['ultralow', 'low', 'medium', 'high', 'normal'],
|
|
326
|
+
}
|
|
327
|
+
```
|
|
455
328
|
|
|
456
|
-
|
|
329
|
+
### CORS (Cross-Origin)
|
|
457
330
|
|
|
458
|
-
|
|
331
|
+
Required when client and API are on different domains:
|
|
459
332
|
|
|
460
333
|
```typescript
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
path: '/var/data/drive',
|
|
468
|
-
google: {
|
|
469
|
-
clientId: process.env.GOOGLE_CLIENT_ID!,
|
|
470
|
-
clientSecret: process.env.GOOGLE_CLIENT_SECRET!,
|
|
471
|
-
redirectUri: process.env.GOOGLE_REDIRECT_URI!,
|
|
472
|
-
},
|
|
473
|
-
},
|
|
474
|
-
// ... other config options
|
|
475
|
-
});
|
|
334
|
+
cors: {
|
|
335
|
+
enabled: true,
|
|
336
|
+
origins: ['https://app.example.com'],
|
|
337
|
+
credentials: true, // Allow cookies/auth headers
|
|
338
|
+
maxAge: 86400, // Preflight cache (24 hours)
|
|
339
|
+
}
|
|
476
340
|
```
|
|
477
341
|
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
Add to your `.env` file:
|
|
342
|
+
**Client setup for CORS:**
|
|
481
343
|
|
|
482
|
-
```
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
344
|
+
```tsx
|
|
345
|
+
<DriveProvider apiEndpoint='https://api.example.com/drive' withCredentials={true}>
|
|
346
|
+
{children}
|
|
347
|
+
</DriveProvider>
|
|
486
348
|
```
|
|
487
349
|
|
|
488
|
-
|
|
350
|
+
| Option | Type | Default | Description |
|
|
351
|
+
| ---------------- | -------------------- | ----------------------------------------------------------- | ------------------------------- |
|
|
352
|
+
| `enabled` | `boolean` | `false` | Enable CORS |
|
|
353
|
+
| `origins` | `string \| string[]` | `'*'` | Allowed origins |
|
|
354
|
+
| `methods` | `string[]` | `['GET', 'POST', 'PUT', 'DELETE', 'OPTIONS']` | Allowed HTTP methods |
|
|
355
|
+
| `allowedHeaders` | `string[]` | `['Content-Type', 'Authorization', 'X-Drive-Account']` | Headers clients can send |
|
|
356
|
+
| `exposedHeaders` | `string[]` | `['Content-Length', 'Content-Type', 'Content-Disposition']` | Headers exposed to client |
|
|
357
|
+
| `credentials` | `boolean` | `false` | Allow credentials |
|
|
358
|
+
| `maxAge` | `number` | `86400` | Preflight cache duration (secs) |
|
|
489
359
|
|
|
490
|
-
|
|
360
|
+
> When `credentials: true`, you must specify explicit origins (not `'*'`).
|
|
491
361
|
|
|
492
|
-
|
|
493
|
-
| ------------------------------------------------ | ---------------------------------------------------------------- |
|
|
494
|
-
| `https://www.googleapis.com/auth/drive` | Full access to all Drive files (recommended for file management) |
|
|
495
|
-
| `https://www.googleapis.com/auth/drive.file` | Access only to files created/opened by your app |
|
|
496
|
-
| `https://www.googleapis.com/auth/drive.readonly` | Read-only access to all Drive files |
|
|
362
|
+
---
|
|
497
363
|
|
|
498
|
-
|
|
364
|
+
## Google Drive Integration
|
|
499
365
|
|
|
500
|
-
|
|
501
|
-
const SCOPES = ['https://www.googleapis.com/auth/drive'];
|
|
502
|
-
```
|
|
366
|
+
### 1. Google Cloud Setup
|
|
503
367
|
|
|
504
|
-
|
|
368
|
+
1. Go to [Google Cloud Console](https://console.cloud.google.com/)
|
|
369
|
+
2. Create/select a project
|
|
370
|
+
3. Enable **Google Drive API**
|
|
371
|
+
4. Create OAuth 2.0 credentials (Web application)
|
|
372
|
+
5. Add redirect URI (e.g., `http://localhost:3000/api/drive?action=callback`)
|
|
505
373
|
|
|
506
|
-
|
|
374
|
+
### 2. Configuration
|
|
507
375
|
|
|
508
376
|
```typescript
|
|
509
|
-
{
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
refresh_token: '...',
|
|
517
|
-
expiry_date: 1234567890
|
|
518
|
-
}
|
|
519
|
-
}
|
|
520
|
-
}
|
|
377
|
+
storage: {
|
|
378
|
+
path: '/var/data/drive',
|
|
379
|
+
google: {
|
|
380
|
+
clientId: process.env.GOOGLE_CLIENT_ID!,
|
|
381
|
+
clientSecret: process.env.GOOGLE_CLIENT_SECRET!,
|
|
382
|
+
redirectUri: process.env.GOOGLE_REDIRECT_URI!,
|
|
383
|
+
},
|
|
521
384
|
}
|
|
522
385
|
```
|
|
523
386
|
|
|
524
|
-
|
|
387
|
+
### 3. Environment Variables
|
|
388
|
+
|
|
389
|
+
```env
|
|
390
|
+
GOOGLE_CLIENT_ID=your-client-id.apps.googleusercontent.com
|
|
391
|
+
GOOGLE_CLIENT_SECRET=your-client-secret
|
|
392
|
+
GOOGLE_REDIRECT_URI=http://localhost:3000/api/drive?action=callback
|
|
393
|
+
```
|
|
394
|
+
|
|
395
|
+
### OAuth Scopes
|
|
525
396
|
|
|
526
|
-
|
|
397
|
+
| Scope | Description |
|
|
398
|
+
| ------------------------------------------------ | ---------------------- |
|
|
399
|
+
| `https://www.googleapis.com/auth/drive` | Full Drive access |
|
|
400
|
+
| `https://www.googleapis.com/auth/drive.file` | App-created files only |
|
|
401
|
+
| `https://www.googleapis.com/auth/drive.readonly` | Read-only access |
|
|
527
402
|
|
|
528
|
-
|
|
529
|
-
- **Mobile**: Optimizes for small screens by separating the Search bar into a full-width top row and grouping action buttons in a scrollable toolbar below.
|
|
403
|
+
---
|
|
530
404
|
|
|
531
405
|
## API Endpoints
|
|
532
406
|
|
|
533
|
-
All operations use
|
|
534
|
-
|
|
535
|
-
| Action | Method | Description
|
|
536
|
-
| ----------------- | ------ |
|
|
537
|
-
| `upload` | POST | Chunked file upload
|
|
538
|
-
| `list` | GET | List
|
|
539
|
-
| `serve` | GET | Serve file
|
|
540
|
-
| `thumbnail` | GET |
|
|
541
|
-
| `rename` | PATCH | Rename
|
|
542
|
-
| `trash` | POST | Move
|
|
543
|
-
| `deletePermanent` | DELETE |
|
|
544
|
-
| `restore` | POST | Restore
|
|
545
|
-
| `emptyTrash` | DELETE |
|
|
546
|
-
| `createFolder` | POST | Create
|
|
547
|
-
| `move` | POST | Move
|
|
548
|
-
| `search` | GET | Search by name
|
|
549
|
-
| `quota` | GET | Get
|
|
407
|
+
All operations use `?action=` query parameter:
|
|
408
|
+
|
|
409
|
+
| Action | Method | Description |
|
|
410
|
+
| ----------------- | ------ | -------------------------------- |
|
|
411
|
+
| `upload` | POST | Chunked file upload |
|
|
412
|
+
| `list` | GET | List folder contents |
|
|
413
|
+
| `serve` | GET | Serve file (with resize/convert) |
|
|
414
|
+
| `thumbnail` | GET | Get file thumbnail |
|
|
415
|
+
| `rename` | PATCH | Rename file/folder |
|
|
416
|
+
| `trash` | POST | Move to trash |
|
|
417
|
+
| `deletePermanent` | DELETE | Delete permanently |
|
|
418
|
+
| `restore` | POST | Restore from trash |
|
|
419
|
+
| `emptyTrash` | DELETE | Empty all trash |
|
|
420
|
+
| `createFolder` | POST | Create folder |
|
|
421
|
+
| `move` | POST | Move to new parent |
|
|
422
|
+
| `search` | GET | Search by name |
|
|
423
|
+
| `quota` | GET | Get storage usage |
|
|
424
|
+
|
|
425
|
+
---
|
|
550
426
|
|
|
551
427
|
## License
|
|
552
428
|
|