@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 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
- - 📁 **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
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: ['./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}'],
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 '@muhgholy/next-drive/server';
69
- import type { TDriveConfigInformation } from '@muhgholy/next-drive/server';
68
+ import { driveConfiguration } from "@muhgholy/next-drive/server";
69
+ import type { TDriveConfigInformation } from "@muhgholy/next-drive/server";
70
70
 
71
71
  driveConfiguration({
72
- database: 'MONGOOSE',
73
- apiUrl: '/api/drive',
74
- storage: { path: '/var/data/drive' },
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: ['image/*', 'video/*', 'application/pdf'],
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('Unauthenticated');
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 '@/lib/drive';
102
- import { driveAPIHandler } from '@muhgholy/next-drive/server';
103
- import type { NextApiRequest, NextApiResponse } from 'next';
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['content-type']?.includes('application/json')) {
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 '@muhgholy/next-drive/client';
130
+ import { DriveProvider } from "@muhgholy/next-drive/client";
131
131
 
132
132
  export default function RootLayout({ children }) {
133
- return <DriveProvider apiEndpoint='/api/drive'>{children}</DriveProvider>;
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 '@muhgholy/next-drive/client';
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 'react';
153
- import { DriveFileChooser } from '@muhgholy/next-drive/client';
154
- import type { TDriveFile } from '@muhgholy/next-drive/client';
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='image/*' />;
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 '@muhgholy/next-drive/server/express';
171
- import type { TDriveConfigInformation } from '@muhgholy/next-drive/server/express';
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: 'MONGOOSE',
175
- apiUrl: '/api/drive',
176
- storage: { path: '/var/data/drive' },
174
+ database: "MONGOOSE",
175
+ apiUrl: "/api/drive",
176
+ storage: { path: "/var/data/drive" },
177
177
  security: {
178
178
  maxUploadSizeInBytes: 50 * 1024 * 1024,
179
- allowedMimeTypes: ['image/*', 'video/*', 'application/pdf'],
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('Unauthenticated');
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 './lib/drive';
195
- import express from 'express';
196
- import { driveAPIHandlerExpress } from '@muhgholy/next-drive/server/express';
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('/drive', driveAPIHandlerExpress);
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 'zod';
214
- import { driveFileSchemaZod } from '@muhgholy/next-drive/schemas';
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 '@muhgholy/next-drive/client';
232
- import type { TDriveFile } from '@muhgholy/next-drive/client';
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: 'medium', format: 'webp' });
241
+ const optimizedUrl = createUrl(driveFile, { quality: "medium", format: "webp" });
242
242
 
243
243
  // Responsive srcSet for images
244
- const { srcSet, sizes } = createSrcSet(driveFile, 'webp');
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 '@muhgholy/next-drive/server';
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('2026-12-31') });
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 '@muhgholy/next-drive/server';
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 '@muhgholy/next-drive/server';
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('output.jpg');
334
+ await sharp(path).resize(800, 600).toFile("output.jpg");
294
335
 
295
336
  // Use with FFmpeg
296
- await ffmpeg(path).format('mp4').save('output.mp4');
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='https://api.example.com/drive' withCredentials={true}>
386
+ <DriveProvider apiEndpoint="https://api.example.com/drive" withCredentials={true}>
346
387
  {children}
347
388
  </DriveProvider>
348
389
  ```