@muhgholy/next-drive 2.2.5 → 3.1.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-EV3BVXQN.js → chunk-F5ZVJGYN.js} +226 -63
- 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/components/ui/dialog-fullscreen.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.css +59 -0
- package/dist/client/index.css.map +1 -0
- package/dist/client/index.d.ts +1 -0
- package/dist/client/index.d.ts.map +1 -1
- package/dist/client/index.js +134 -87
- package/dist/client/index.js.map +1 -1
- package/dist/client/styles.css +0 -4
- 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/server/utils.d.ts +0 -5
- package/dist/server/utils.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 +1 -5
- package/dist/chunk-EV3BVXQN.js.map +0 -1
- package/dist/client/styles.js +0 -2
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
|
```
|