@digibuffer/file-manager-core 1.0.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 +240 -0
- package/dist/adapters/next.d.ts +24 -0
- package/dist/adapters/next.js +46 -0
- package/dist/index.d.ts +123 -0
- package/dist/index.js +3 -0
- package/dist/router-CY6g7nWy.d.ts +339 -0
- package/dist/router-De_vaLLo.js +1031 -0
- package/package.json +70 -0
package/README.md
ADDED
|
@@ -0,0 +1,240 @@
|
|
|
1
|
+
# @digibuffer/file-manager-core
|
|
2
|
+
|
|
3
|
+
Complete file management library for cloud storage (R2/S3) with optional PostgreSQL database integration.
|
|
4
|
+
|
|
5
|
+
## Features
|
|
6
|
+
|
|
7
|
+
- ✅ **Full CRUD Operations**: List, Move, Rename, Copy, Delete files
|
|
8
|
+
- ✅ **Dual Mode**: Storage-only or Database-backed operations
|
|
9
|
+
- ✅ **PostgreSQL Integration**: Store file metadata in database
|
|
10
|
+
- ✅ **Auto-Sync**: Automatic synchronization between cloud and database
|
|
11
|
+
- ✅ **Batch Operations**: Efficient batch delete and operations
|
|
12
|
+
- ✅ **Presigned URLs**: Generate secure download links
|
|
13
|
+
- ✅ **Pagination**: Built-in cursor-based pagination
|
|
14
|
+
- ✅ **TypeScript**: Full type safety
|
|
15
|
+
- ✅ **Next.js Adapter**: Easy integration with Next.js
|
|
16
|
+
|
|
17
|
+
## Installation
|
|
18
|
+
|
|
19
|
+
```bash
|
|
20
|
+
npm install @digibuffer/file-manager-core
|
|
21
|
+
# Optional: for database support
|
|
22
|
+
npm install postgres
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
## Quick Start
|
|
26
|
+
|
|
27
|
+
### Storage-Only Mode
|
|
28
|
+
|
|
29
|
+
```typescript
|
|
30
|
+
import { FileManager } from '@digibuffer/file-manager-core';
|
|
31
|
+
|
|
32
|
+
const fileManager = new FileManager({
|
|
33
|
+
storage: {
|
|
34
|
+
bucket: 'my-bucket',
|
|
35
|
+
accessKeyId: process.env.R2_ACCESS_KEY_ID!,
|
|
36
|
+
secretAccessKey: process.env.R2_SECRET_ACCESS_KEY!,
|
|
37
|
+
endpoint: process.env.R2_ENDPOINT!,
|
|
38
|
+
},
|
|
39
|
+
});
|
|
40
|
+
|
|
41
|
+
// List files
|
|
42
|
+
const files = await fileManager.list({ prefix: 'uploads/' });
|
|
43
|
+
|
|
44
|
+
// Move file
|
|
45
|
+
await fileManager.move({
|
|
46
|
+
sourceKey: 'old/path/file.pdf',
|
|
47
|
+
destinationKey: 'new/path/file.pdf',
|
|
48
|
+
});
|
|
49
|
+
|
|
50
|
+
// Copy file
|
|
51
|
+
await fileManager.copy({
|
|
52
|
+
sourceKey: 'original.pdf',
|
|
53
|
+
destinationKey: 'copy.pdf',
|
|
54
|
+
});
|
|
55
|
+
|
|
56
|
+
// Delete file
|
|
57
|
+
await fileManager.delete('file-to-delete.pdf');
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
### Database Mode (with PostgreSQL)
|
|
61
|
+
|
|
62
|
+
```typescript
|
|
63
|
+
import { FileManager } from '@digibuffer/file-manager-core';
|
|
64
|
+
|
|
65
|
+
const fileManager = new FileManager({
|
|
66
|
+
storage: {
|
|
67
|
+
bucket: 'my-bucket',
|
|
68
|
+
accessKeyId: process.env.R2_ACCESS_KEY_ID!,
|
|
69
|
+
secretAccessKey: process.env.R2_SECRET_ACCESS_KEY!,
|
|
70
|
+
endpoint: process.env.R2_ENDPOINT!,
|
|
71
|
+
},
|
|
72
|
+
database: {
|
|
73
|
+
connectionString: process.env.DATABASE_URL,
|
|
74
|
+
tableName: 'files', // optional, defaults to 'files'
|
|
75
|
+
},
|
|
76
|
+
defaultSource: 'database', // 'storage' | 'database' | 'both'
|
|
77
|
+
autoSync: true, // auto-sync storage changes to database
|
|
78
|
+
});
|
|
79
|
+
|
|
80
|
+
// List from database (faster)
|
|
81
|
+
const files = await fileManager.list({ prefix: 'uploads/' });
|
|
82
|
+
|
|
83
|
+
// List from both and merge
|
|
84
|
+
const allFiles = await fileManager.list({}, 'both');
|
|
85
|
+
|
|
86
|
+
// Sync storage to database
|
|
87
|
+
await fileManager.syncToDatabase();
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
## API Reference
|
|
91
|
+
|
|
92
|
+
### Core Methods
|
|
93
|
+
|
|
94
|
+
#### `list(options, source?)`
|
|
95
|
+
List files with pagination and filtering.
|
|
96
|
+
|
|
97
|
+
```typescript
|
|
98
|
+
const result = await fileManager.list({
|
|
99
|
+
prefix: 'folder/',
|
|
100
|
+
maxKeys: 100,
|
|
101
|
+
sortBy: 'lastModified',
|
|
102
|
+
sortOrder: 'desc',
|
|
103
|
+
});
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
#### `move(options)`
|
|
107
|
+
Move a file to a new location.
|
|
108
|
+
|
|
109
|
+
```typescript
|
|
110
|
+
await fileManager.move({
|
|
111
|
+
sourceKey: 'old/file.pdf',
|
|
112
|
+
destinationKey: 'new/file.pdf',
|
|
113
|
+
});
|
|
114
|
+
```
|
|
115
|
+
|
|
116
|
+
#### `copy(options)`
|
|
117
|
+
Copy a file to a new location.
|
|
118
|
+
|
|
119
|
+
```typescript
|
|
120
|
+
await fileManager.copy({
|
|
121
|
+
sourceKey: 'original.pdf',
|
|
122
|
+
destinationKey: 'copy.pdf',
|
|
123
|
+
metadata: { copied: 'true' },
|
|
124
|
+
});
|
|
125
|
+
```
|
|
126
|
+
|
|
127
|
+
#### `rename(oldKey, newKey)`
|
|
128
|
+
Rename a file (alias for move).
|
|
129
|
+
|
|
130
|
+
```typescript
|
|
131
|
+
await fileManager.rename('old-name.pdf', 'new-name.pdf');
|
|
132
|
+
```
|
|
133
|
+
|
|
134
|
+
#### `delete(key)`
|
|
135
|
+
Delete a single file.
|
|
136
|
+
|
|
137
|
+
```typescript
|
|
138
|
+
await fileManager.delete('file.pdf');
|
|
139
|
+
```
|
|
140
|
+
|
|
141
|
+
#### `deleteBatch(keys)`
|
|
142
|
+
Delete multiple files.
|
|
143
|
+
|
|
144
|
+
```typescript
|
|
145
|
+
await fileManager.deleteBatch(['file1.pdf', 'file2.pdf']);
|
|
146
|
+
```
|
|
147
|
+
|
|
148
|
+
#### `getDownloadUrl(key, options?)`
|
|
149
|
+
Generate presigned download URL.
|
|
150
|
+
|
|
151
|
+
```typescript
|
|
152
|
+
const url = await fileManager.getDownloadUrl('file.pdf', {
|
|
153
|
+
expiresIn: 3600,
|
|
154
|
+
disposition: 'attachment',
|
|
155
|
+
filename: 'download.pdf',
|
|
156
|
+
});
|
|
157
|
+
```
|
|
158
|
+
|
|
159
|
+
#### `exists(key, source?)`
|
|
160
|
+
Check if file exists.
|
|
161
|
+
|
|
162
|
+
```typescript
|
|
163
|
+
const exists = await fileManager.exists('file.pdf');
|
|
164
|
+
```
|
|
165
|
+
|
|
166
|
+
#### `getMetadata(key, source?)`
|
|
167
|
+
Get file metadata.
|
|
168
|
+
|
|
169
|
+
```typescript
|
|
170
|
+
const metadata = await fileManager.getMetadata('file.pdf');
|
|
171
|
+
```
|
|
172
|
+
|
|
173
|
+
### Next.js Integration
|
|
174
|
+
|
|
175
|
+
```typescript
|
|
176
|
+
// app/api/files/route.ts
|
|
177
|
+
import { FileManager, createFileManagerRouter } from '@digibuffer/file-manager-core';
|
|
178
|
+
import { toRouteHandler, withCors } from '@digibuffer/file-manager-core/adapters/next';
|
|
179
|
+
|
|
180
|
+
const fileManager = new FileManager({
|
|
181
|
+
storage: { /* config */ },
|
|
182
|
+
database: { /* config */ },
|
|
183
|
+
});
|
|
184
|
+
|
|
185
|
+
const router = createFileManagerRouter({
|
|
186
|
+
fileManager,
|
|
187
|
+
authorize: async (context, operation, resource) => {
|
|
188
|
+
// Your authorization logic
|
|
189
|
+
return true;
|
|
190
|
+
},
|
|
191
|
+
getAuthContext: async (request) => {
|
|
192
|
+
// Extract user from session/token
|
|
193
|
+
return { userId: 'user-123' };
|
|
194
|
+
},
|
|
195
|
+
});
|
|
196
|
+
|
|
197
|
+
export const POST = withCors(toRouteHandler(router));
|
|
198
|
+
```
|
|
199
|
+
|
|
200
|
+
### Operation Hooks
|
|
201
|
+
|
|
202
|
+
```typescript
|
|
203
|
+
const fileManager = new FileManager(config, {
|
|
204
|
+
beforeDelete: async (keys) => {
|
|
205
|
+
console.log('About to delete:', keys);
|
|
206
|
+
},
|
|
207
|
+
afterDelete: async (keys) => {
|
|
208
|
+
console.log('Deleted:', keys);
|
|
209
|
+
},
|
|
210
|
+
beforeMove: async (options) => {
|
|
211
|
+
console.log('Moving:', options);
|
|
212
|
+
},
|
|
213
|
+
afterMove: async (options) => {
|
|
214
|
+
console.log('Moved:', options);
|
|
215
|
+
},
|
|
216
|
+
});
|
|
217
|
+
```
|
|
218
|
+
|
|
219
|
+
## Database Schema
|
|
220
|
+
|
|
221
|
+
The library automatically creates this table:
|
|
222
|
+
|
|
223
|
+
```sql
|
|
224
|
+
CREATE TABLE files (
|
|
225
|
+
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
|
226
|
+
key TEXT NOT NULL UNIQUE,
|
|
227
|
+
size BIGINT NOT NULL,
|
|
228
|
+
content_type TEXT,
|
|
229
|
+
etag TEXT,
|
|
230
|
+
user_id TEXT,
|
|
231
|
+
custom_metadata JSONB,
|
|
232
|
+
last_modified TIMESTAMP NOT NULL,
|
|
233
|
+
uploaded_at TIMESTAMP NOT NULL DEFAULT NOW(),
|
|
234
|
+
updated_at TIMESTAMP NOT NULL DEFAULT NOW()
|
|
235
|
+
);
|
|
236
|
+
```
|
|
237
|
+
|
|
238
|
+
## License
|
|
239
|
+
|
|
240
|
+
MIT
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { t as FileManagerRouter } from "../router-CY6g7nWy.js";
|
|
2
|
+
import { NextRequest } from "next/server";
|
|
3
|
+
|
|
4
|
+
//#region src/adapters/next.d.ts
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Convert FileManagerRouter to Next.js App Router handler
|
|
8
|
+
*/
|
|
9
|
+
declare function toRouteHandler(router: FileManagerRouter): (request: NextRequest) => Promise<Response>;
|
|
10
|
+
/**
|
|
11
|
+
* CORS options
|
|
12
|
+
*/
|
|
13
|
+
interface CorsOptions {
|
|
14
|
+
origin?: string | string[];
|
|
15
|
+
methods?: string[];
|
|
16
|
+
allowedHeaders?: string[];
|
|
17
|
+
credentials?: boolean;
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* Add CORS headers to handler
|
|
21
|
+
*/
|
|
22
|
+
declare function withCors<T extends Request = Request>(handler: (request: T) => Promise<Response>, options?: CorsOptions): (request: T) => Promise<Response>;
|
|
23
|
+
//#endregion
|
|
24
|
+
export { toRouteHandler, withCors };
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import "../router-De_vaLLo.js";
|
|
2
|
+
|
|
3
|
+
//#region src/adapters/next.ts
|
|
4
|
+
/**
|
|
5
|
+
* Convert FileManagerRouter to Next.js App Router handler
|
|
6
|
+
*/
|
|
7
|
+
function toRouteHandler(router) {
|
|
8
|
+
return async (request) => {
|
|
9
|
+
return router.handle(request);
|
|
10
|
+
};
|
|
11
|
+
}
|
|
12
|
+
/**
|
|
13
|
+
* Add CORS headers to handler
|
|
14
|
+
*/
|
|
15
|
+
function withCors(handler, options = {}) {
|
|
16
|
+
const { origin = "*", methods = [
|
|
17
|
+
"GET",
|
|
18
|
+
"POST",
|
|
19
|
+
"PUT",
|
|
20
|
+
"DELETE",
|
|
21
|
+
"OPTIONS"
|
|
22
|
+
], allowedHeaders = ["Content-Type", "Authorization"], credentials = false } = options;
|
|
23
|
+
return async (request) => {
|
|
24
|
+
if (request.method === "OPTIONS") return new Response(null, {
|
|
25
|
+
status: 204,
|
|
26
|
+
headers: {
|
|
27
|
+
"Access-Control-Allow-Origin": Array.isArray(origin) ? origin[0] : origin,
|
|
28
|
+
"Access-Control-Allow-Methods": methods.join(", "),
|
|
29
|
+
"Access-Control-Allow-Headers": allowedHeaders.join(", "),
|
|
30
|
+
...credentials && { "Access-Control-Allow-Credentials": "true" }
|
|
31
|
+
}
|
|
32
|
+
});
|
|
33
|
+
const response = await handler(request);
|
|
34
|
+
const corsHeaders = new Headers(response.headers);
|
|
35
|
+
corsHeaders.set("Access-Control-Allow-Origin", Array.isArray(origin) ? origin[0] : origin);
|
|
36
|
+
if (credentials) corsHeaders.set("Access-Control-Allow-Credentials", "true");
|
|
37
|
+
return new Response(response.body, {
|
|
38
|
+
status: response.status,
|
|
39
|
+
statusText: response.statusText,
|
|
40
|
+
headers: corsHeaders
|
|
41
|
+
});
|
|
42
|
+
};
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
//#endregion
|
|
46
|
+
export { toRouteHandler, withCors };
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
import { C as PaginatedResponse, D as StorageFile, E as StorageConfig, O as StorageProvider, S as OperationResult, T as RequestAction, _ as FileOperationResponse, a as AuthorizationCallback, b as MoveOptions, c as DataSource, d as DatabaseProvider, f as DownloadUrlOptions, g as FileOperationRequest, h as FileMetadata, i as AuthContext, l as DatabaseConfig, m as FileManagerError, n as createFileManagerRouter, o as BatchOperationResult, p as FileManagerConfig, r as FileManager, s as CopyOptions, t as FileManagerRouter, u as DatabaseFile, v as ListOptions, w as PaginationCursor, x as OperationHooks, y as ManagedFile } from "./router-CY6g7nWy.js";
|
|
2
|
+
|
|
3
|
+
//#region src/storage/s3-provider.d.ts
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* S3/R2 Storage Provider Implementation
|
|
7
|
+
*/
|
|
8
|
+
declare class S3StorageProvider implements StorageProvider {
|
|
9
|
+
private config;
|
|
10
|
+
constructor(config: StorageConfig);
|
|
11
|
+
/**
|
|
12
|
+
* List files from S3/R2 storage
|
|
13
|
+
*/
|
|
14
|
+
list(options?: ListOptions): Promise<PaginatedResponse<StorageFile>>;
|
|
15
|
+
/**
|
|
16
|
+
* Delete a single file
|
|
17
|
+
*/
|
|
18
|
+
delete(key: string): Promise<OperationResult>;
|
|
19
|
+
/**
|
|
20
|
+
* Delete multiple files in batch
|
|
21
|
+
*/
|
|
22
|
+
deleteBatch(keys: string[]): Promise<BatchOperationResult>;
|
|
23
|
+
/**
|
|
24
|
+
* Check if file exists
|
|
25
|
+
*/
|
|
26
|
+
exists(key: string): Promise<boolean>;
|
|
27
|
+
/**
|
|
28
|
+
* Get file metadata
|
|
29
|
+
*/
|
|
30
|
+
getMetadata(key: string): Promise<StorageFile | null>;
|
|
31
|
+
/**
|
|
32
|
+
* Generate presigned download URL
|
|
33
|
+
*/
|
|
34
|
+
getDownloadUrl(key: string, options?: DownloadUrlOptions): Promise<string>;
|
|
35
|
+
/**
|
|
36
|
+
* Move a file (copy + delete source)
|
|
37
|
+
*/
|
|
38
|
+
move(options: MoveOptions): Promise<OperationResult>;
|
|
39
|
+
/**
|
|
40
|
+
* Copy a file to a new location
|
|
41
|
+
*/
|
|
42
|
+
copy(options: CopyOptions): Promise<OperationResult>;
|
|
43
|
+
/**
|
|
44
|
+
* Rename a file (same as move)
|
|
45
|
+
*/
|
|
46
|
+
rename(oldKey: string, newKey: string): Promise<OperationResult>;
|
|
47
|
+
/**
|
|
48
|
+
* Parse XML list response
|
|
49
|
+
*/
|
|
50
|
+
private parseListResponse;
|
|
51
|
+
/**
|
|
52
|
+
* Extract continuation token from XML
|
|
53
|
+
*/
|
|
54
|
+
private extractContinuationToken;
|
|
55
|
+
/**
|
|
56
|
+
* Extract value from XML tag
|
|
57
|
+
*/
|
|
58
|
+
private extractXmlValue;
|
|
59
|
+
/**
|
|
60
|
+
* Sort files by specified field
|
|
61
|
+
*/
|
|
62
|
+
private sortFiles;
|
|
63
|
+
}
|
|
64
|
+
//#endregion
|
|
65
|
+
//#region src/database/postgres-provider.d.ts
|
|
66
|
+
/**
|
|
67
|
+
* PostgreSQL Database Provider Implementation
|
|
68
|
+
*/
|
|
69
|
+
declare class PostgresDatabaseProvider implements DatabaseProvider {
|
|
70
|
+
private sql;
|
|
71
|
+
private tableName;
|
|
72
|
+
constructor(config: DatabaseConfig);
|
|
73
|
+
/**
|
|
74
|
+
* Initialize PostgreSQL connection
|
|
75
|
+
*/
|
|
76
|
+
private initializeConnection;
|
|
77
|
+
/**
|
|
78
|
+
* Create files table if it doesn't exist
|
|
79
|
+
*/
|
|
80
|
+
private ensureTableExists;
|
|
81
|
+
/**
|
|
82
|
+
* List files from database
|
|
83
|
+
*/
|
|
84
|
+
list(options?: ListOptions): Promise<PaginatedResponse<DatabaseFile>>;
|
|
85
|
+
/**
|
|
86
|
+
* Get a single file by key
|
|
87
|
+
*/
|
|
88
|
+
get(key: string): Promise<DatabaseFile | null>;
|
|
89
|
+
/**
|
|
90
|
+
* Create a new file record
|
|
91
|
+
*/
|
|
92
|
+
create(file: Omit<DatabaseFile, 'id' | 'uploadedAt' | 'updatedAt'>): Promise<DatabaseFile>;
|
|
93
|
+
/**
|
|
94
|
+
* Update file metadata
|
|
95
|
+
*/
|
|
96
|
+
update(key: string, data: Partial<DatabaseFile>): Promise<DatabaseFile | null>;
|
|
97
|
+
/**
|
|
98
|
+
* Delete a file record
|
|
99
|
+
*/
|
|
100
|
+
delete(key: string): Promise<OperationResult>;
|
|
101
|
+
/**
|
|
102
|
+
* Delete multiple file records
|
|
103
|
+
*/
|
|
104
|
+
deleteBatch(keys: string[]): Promise<BatchOperationResult>;
|
|
105
|
+
/**
|
|
106
|
+
* Check if file exists
|
|
107
|
+
*/
|
|
108
|
+
exists(key: string): Promise<boolean>;
|
|
109
|
+
/**
|
|
110
|
+
* Sync storage files to database
|
|
111
|
+
*/
|
|
112
|
+
sync(storageFiles: StorageFile[]): Promise<void>;
|
|
113
|
+
/**
|
|
114
|
+
* Get sort column for SQL query
|
|
115
|
+
*/
|
|
116
|
+
private getSortColumn;
|
|
117
|
+
/**
|
|
118
|
+
* Close database connection
|
|
119
|
+
*/
|
|
120
|
+
close(): Promise<void>;
|
|
121
|
+
}
|
|
122
|
+
//#endregion
|
|
123
|
+
export { type AuthContext, type AuthorizationCallback, type BatchOperationResult, type CopyOptions, type DataSource, type DatabaseConfig, type DatabaseFile, type DatabaseProvider, type DownloadUrlOptions, FileManager, type FileManagerConfig, FileManagerError, FileManagerRouter, type FileMetadata, type FileOperationRequest, type FileOperationResponse, type ListOptions, type ManagedFile, type MoveOptions, type OperationHooks, type OperationResult, type PaginatedResponse, type PaginationCursor, PostgresDatabaseProvider, type RequestAction, S3StorageProvider, type StorageConfig, type StorageFile, type StorageProvider, createFileManagerRouter };
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
import { a as S3StorageProvider, i as PostgresDatabaseProvider, n as createFileManagerRouter, o as FileManagerError, r as FileManager, t as FileManagerRouter } from "./router-De_vaLLo.js";
|
|
2
|
+
|
|
3
|
+
export { FileManager, FileManagerError, FileManagerRouter, PostgresDatabaseProvider, S3StorageProvider, createFileManagerRouter };
|