@digibuffer/file-manager 1.0.1
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 +213 -0
- package/dist/client/index.d.ts +3 -0
- package/dist/client/index.js +3 -0
- package/dist/client-DTkToHgY.js +347 -0
- package/dist/index-CHmsNSsb.d.ts +1 -0
- package/dist/index-pUAG6PPj.d.ts +132 -0
- package/dist/index.d.ts +5 -0
- package/dist/index.js +4 -0
- package/dist/router-CD_RQnlx.d.ts +159 -0
- package/dist/server/adapters/next.d.ts +20 -0
- package/dist/server/adapters/next.js +63 -0
- package/dist/server/index.d.ts +4 -0
- package/dist/server/index.js +3 -0
- package/dist/server-CZrPOP0h.js +490 -0
- package/dist/types-Brk9aR7c.d.ts +177 -0
- package/package.json +70 -0
package/README.md
ADDED
|
@@ -0,0 +1,213 @@
|
|
|
1
|
+
# @digibuffer/file-manager
|
|
2
|
+
|
|
3
|
+
File management library for fetching, deleting, and managing files in R2/S3 with database operations and pagination support.
|
|
4
|
+
|
|
5
|
+
## Features
|
|
6
|
+
|
|
7
|
+
- 📂 **List files** with pagination from R2/S3 or database
|
|
8
|
+
- 🗑️ **Delete files** with database sync callbacks
|
|
9
|
+
- 🔗 **Generate download URLs** with presigned URLs
|
|
10
|
+
- 📊 **Pagination support** with cursor-based navigation
|
|
11
|
+
- 🔄 **Database integration** with flexible callbacks
|
|
12
|
+
- 🎯 **Type-safe** with full TypeScript support
|
|
13
|
+
- ⚛️ **React hooks** for easy client-side integration
|
|
14
|
+
- 🚀 **Next.js adapter** for serverless functions
|
|
15
|
+
|
|
16
|
+
## Installation
|
|
17
|
+
|
|
18
|
+
```bash
|
|
19
|
+
npm install @digibuffer/file-manager @digibuffer/upload-lib-server
|
|
20
|
+
# or
|
|
21
|
+
pnpm add @digibuffer/file-manager @digibuffer/upload-lib-server
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
## Quick Start
|
|
25
|
+
|
|
26
|
+
### Server Setup
|
|
27
|
+
|
|
28
|
+
```typescript
|
|
29
|
+
import { cloudflareClient } from '@digibuffer/upload-lib-server/clients';
|
|
30
|
+
import { FileManager, createFileManagerRouter } from '@digibuffer/file-manager/server';
|
|
31
|
+
import { toRouteHandler } from '@digibuffer/file-manager/adapters/next';
|
|
32
|
+
|
|
33
|
+
// Create S3 client
|
|
34
|
+
const client = cloudflareClient({
|
|
35
|
+
accountId: process.env.CLOUDFLARE_ACCOUNT_ID!,
|
|
36
|
+
accessKeyId: process.env.CLOUDFLARE_ACCESS_KEY_ID!,
|
|
37
|
+
secretAccessKey: process.env.CLOUDFLARE_SECRET_ACCESS_KEY!,
|
|
38
|
+
});
|
|
39
|
+
|
|
40
|
+
// Create file manager with database callbacks
|
|
41
|
+
const fileManager = new FileManager({
|
|
42
|
+
client,
|
|
43
|
+
bucketName: 'my-bucket',
|
|
44
|
+
database: {
|
|
45
|
+
onAfterDelete: async ({ key, success }) => {
|
|
46
|
+
if (success) {
|
|
47
|
+
await db.file.delete({ where: { key } });
|
|
48
|
+
}
|
|
49
|
+
},
|
|
50
|
+
getFiles: async ({ limit, cursor }) => {
|
|
51
|
+
const files = await db.file.findMany({
|
|
52
|
+
take: limit,
|
|
53
|
+
skip: cursor ? 1 : 0,
|
|
54
|
+
cursor: cursor ? { id: cursor } : undefined,
|
|
55
|
+
});
|
|
56
|
+
return {
|
|
57
|
+
items: files,
|
|
58
|
+
hasMore: files.length === limit,
|
|
59
|
+
nextCursor: files[files.length - 1]?.id,
|
|
60
|
+
};
|
|
61
|
+
},
|
|
62
|
+
},
|
|
63
|
+
});
|
|
64
|
+
|
|
65
|
+
// Create router
|
|
66
|
+
const router = createFileManagerRouter();
|
|
67
|
+
router.route('default', {
|
|
68
|
+
fileManager,
|
|
69
|
+
onAuthorize: async (request) => {
|
|
70
|
+
// Add your auth logic here
|
|
71
|
+
return true;
|
|
72
|
+
},
|
|
73
|
+
});
|
|
74
|
+
|
|
75
|
+
// Export Next.js handler
|
|
76
|
+
export const POST = toRouteHandler(router);
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
### Client Setup
|
|
80
|
+
|
|
81
|
+
```typescript
|
|
82
|
+
import { FileManagerProvider } from '@digibuffer/file-manager/client';
|
|
83
|
+
|
|
84
|
+
function App() {
|
|
85
|
+
return (
|
|
86
|
+
<FileManagerProvider
|
|
87
|
+
config={{
|
|
88
|
+
endpoint: '/api/files',
|
|
89
|
+
headers: {
|
|
90
|
+
Authorization: `Bearer ${token}`,
|
|
91
|
+
},
|
|
92
|
+
}}
|
|
93
|
+
>
|
|
94
|
+
<FileList />
|
|
95
|
+
</FileManagerProvider>
|
|
96
|
+
);
|
|
97
|
+
}
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
### Using Hooks
|
|
101
|
+
|
|
102
|
+
```typescript
|
|
103
|
+
import { useListFiles, useDeleteFile, useDownloadUrl } from '@digibuffer/file-manager/client';
|
|
104
|
+
|
|
105
|
+
function FileList() {
|
|
106
|
+
const { data, list, loadMore, hasMore, isLoading } = useListFiles();
|
|
107
|
+
const { deleteFile } = useDeleteFile();
|
|
108
|
+
const { download } = useDownloadUrl();
|
|
109
|
+
|
|
110
|
+
useEffect(() => {
|
|
111
|
+
list({ limit: 20 });
|
|
112
|
+
}, []);
|
|
113
|
+
|
|
114
|
+
return (
|
|
115
|
+
<div>
|
|
116
|
+
{data?.items.map((file) => (
|
|
117
|
+
<div key={file.key}>
|
|
118
|
+
<span>{file.key}</span>
|
|
119
|
+
<button onClick={() => download(file.key)}>Download</button>
|
|
120
|
+
<button onClick={() => deleteFile(file.key)}>Delete</button>
|
|
121
|
+
</div>
|
|
122
|
+
))}
|
|
123
|
+
{hasMore && <button onClick={() => loadMore()}>Load More</button>}
|
|
124
|
+
</div>
|
|
125
|
+
);
|
|
126
|
+
}
|
|
127
|
+
```
|
|
128
|
+
|
|
129
|
+
## API Reference
|
|
130
|
+
|
|
131
|
+
### FileManager
|
|
132
|
+
|
|
133
|
+
Main server-side class for managing files.
|
|
134
|
+
|
|
135
|
+
```typescript
|
|
136
|
+
const fileManager = new FileManager({
|
|
137
|
+
client: Client, // S3 client from upload-lib-server
|
|
138
|
+
bucketName: string,
|
|
139
|
+
database?: DatabaseCallbacks,
|
|
140
|
+
defaultLimit?: number, // Default: 50
|
|
141
|
+
maxLimit?: number, // Default: 1000
|
|
142
|
+
});
|
|
143
|
+
```
|
|
144
|
+
|
|
145
|
+
#### Methods
|
|
146
|
+
|
|
147
|
+
- `list(options)` - List files with pagination
|
|
148
|
+
- `delete(key)` - Delete a single file
|
|
149
|
+
- `deleteBatch(keys)` - Delete multiple files
|
|
150
|
+
- `getDownloadUrl(key, options)` - Generate presigned download URL
|
|
151
|
+
- `exists(key)` - Check if file exists
|
|
152
|
+
- `getMetadata(key)` - Get file metadata
|
|
153
|
+
|
|
154
|
+
### Database Callbacks
|
|
155
|
+
|
|
156
|
+
```typescript
|
|
157
|
+
type DatabaseCallbacks = {
|
|
158
|
+
onAfterDelete?: (params) => Promise<void>;
|
|
159
|
+
onBeforeDelete?: (params) => Promise<boolean>;
|
|
160
|
+
onAfterBatchDelete?: (params) => Promise<void>;
|
|
161
|
+
getFileByKey?: (key) => Promise<File | null>;
|
|
162
|
+
getFiles?: (options) => Promise<PaginatedResponse<File>>;
|
|
163
|
+
updateFile?: (key, data) => Promise<File>;
|
|
164
|
+
};
|
|
165
|
+
```
|
|
166
|
+
|
|
167
|
+
### Client Hooks
|
|
168
|
+
|
|
169
|
+
#### useListFiles()
|
|
170
|
+
|
|
171
|
+
```typescript
|
|
172
|
+
const {
|
|
173
|
+
data, // PaginatedResponse<File> | null
|
|
174
|
+
list, // (options) => Promise<PaginatedResponse>
|
|
175
|
+
loadMore, // () => Promise<PaginatedResponse>
|
|
176
|
+
hasMore, // boolean
|
|
177
|
+
isLoading,
|
|
178
|
+
isError,
|
|
179
|
+
error,
|
|
180
|
+
reset,
|
|
181
|
+
} = useListFiles();
|
|
182
|
+
```
|
|
183
|
+
|
|
184
|
+
#### useDeleteFile()
|
|
185
|
+
|
|
186
|
+
```typescript
|
|
187
|
+
const {
|
|
188
|
+
deleteFile, // (key) => Promise<DeleteResult>
|
|
189
|
+
deleteBatch, // (keys) => Promise<BatchDeleteResult>
|
|
190
|
+
isLoading,
|
|
191
|
+
isError,
|
|
192
|
+
error,
|
|
193
|
+
reset,
|
|
194
|
+
} = useDeleteFile();
|
|
195
|
+
```
|
|
196
|
+
|
|
197
|
+
#### useDownloadUrl()
|
|
198
|
+
|
|
199
|
+
```typescript
|
|
200
|
+
const {
|
|
201
|
+
getUrl, // (key, options) => Promise<string>
|
|
202
|
+
download, // (key, options) => Promise<string>
|
|
203
|
+
url,
|
|
204
|
+
isLoading,
|
|
205
|
+
isError,
|
|
206
|
+
error,
|
|
207
|
+
reset,
|
|
208
|
+
} = useDownloadUrl();
|
|
209
|
+
```
|
|
210
|
+
|
|
211
|
+
## License
|
|
212
|
+
|
|
213
|
+
MIT
|
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
import { c as ManagedFile, d as StorageFile, i as DownloadUrlOptions, l as PaginatedResponse, r as DeleteResult, s as ListFilesOptions, t as BatchDeleteResult, u as PaginationOptions } from "../types-Brk9aR7c.js";
|
|
2
|
+
import { a as useFileManagerConfig, c as ApiSuccessResponse, d as UseDeleteFileState, f as UseDownloadUrlState, i as FileManagerProvider, l as FileManagerClientConfig, n as useDeleteFile, o as ApiErrorResponse, p as UseListFilesState, r as useListFiles, s as ApiResponse, t as useDownloadUrl, u as HookStatus } from "../index-pUAG6PPj.js";
|
|
3
|
+
export { ApiErrorResponse, ApiResponse, ApiSuccessResponse, BatchDeleteResult, DeleteResult, DownloadUrlOptions, FileManagerClientConfig, FileManagerProvider, HookStatus, ListFilesOptions, ManagedFile, PaginatedResponse, PaginationOptions, StorageFile, UseDeleteFileState, UseDownloadUrlState, UseListFilesState, useDeleteFile, useDownloadUrl, useFileManagerConfig, useListFiles };
|
|
@@ -0,0 +1,347 @@
|
|
|
1
|
+
import React, { createContext, useCallback, useContext, useState } from "react";
|
|
2
|
+
import { jsx } from "react/jsx-runtime";
|
|
3
|
+
|
|
4
|
+
//#region src/client/context.tsx
|
|
5
|
+
const FileManagerContext = createContext(null);
|
|
6
|
+
/**
|
|
7
|
+
* Provider for file manager client configuration
|
|
8
|
+
*/
|
|
9
|
+
function FileManagerProvider({ children, config }) {
|
|
10
|
+
return /* @__PURE__ */ jsx(FileManagerContext.Provider, {
|
|
11
|
+
value: config,
|
|
12
|
+
children
|
|
13
|
+
});
|
|
14
|
+
}
|
|
15
|
+
/**
|
|
16
|
+
* Hook to get file manager client configuration
|
|
17
|
+
*/
|
|
18
|
+
function useFileManagerConfig() {
|
|
19
|
+
const config = useContext(FileManagerContext);
|
|
20
|
+
if (!config) throw new Error("useFileManagerConfig must be used within FileManagerProvider");
|
|
21
|
+
return config;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
//#endregion
|
|
25
|
+
//#region src/client/hooks/useListFiles.ts
|
|
26
|
+
/**
|
|
27
|
+
* Hook for listing files with pagination
|
|
28
|
+
*/
|
|
29
|
+
function useListFiles() {
|
|
30
|
+
const config = useFileManagerConfig();
|
|
31
|
+
const [state, setState] = useState({
|
|
32
|
+
data: null,
|
|
33
|
+
status: "idle",
|
|
34
|
+
error: null,
|
|
35
|
+
isLoading: false,
|
|
36
|
+
isError: false,
|
|
37
|
+
isSuccess: false
|
|
38
|
+
});
|
|
39
|
+
const [lastOptions, setLastOptions] = useState({});
|
|
40
|
+
const list = useCallback(async (options = {}) => {
|
|
41
|
+
const { cursor,...optionsWithoutCursor } = options;
|
|
42
|
+
if (!cursor) setLastOptions(optionsWithoutCursor);
|
|
43
|
+
setState((prev) => ({
|
|
44
|
+
...prev,
|
|
45
|
+
status: "loading",
|
|
46
|
+
isLoading: true,
|
|
47
|
+
isError: false,
|
|
48
|
+
error: null
|
|
49
|
+
}));
|
|
50
|
+
try {
|
|
51
|
+
const json = await (await (config.fetch ?? fetch)(config.endpoint, {
|
|
52
|
+
method: "POST",
|
|
53
|
+
headers: {
|
|
54
|
+
"Content-Type": "application/json",
|
|
55
|
+
...config.headers
|
|
56
|
+
},
|
|
57
|
+
body: JSON.stringify({
|
|
58
|
+
action: "list",
|
|
59
|
+
...options
|
|
60
|
+
})
|
|
61
|
+
})).json();
|
|
62
|
+
if (!json.success) throw new Error(json.error?.message ?? "Failed to list files");
|
|
63
|
+
const data = json.data;
|
|
64
|
+
setState({
|
|
65
|
+
data,
|
|
66
|
+
status: "success",
|
|
67
|
+
error: null,
|
|
68
|
+
isLoading: false,
|
|
69
|
+
isError: false,
|
|
70
|
+
isSuccess: true
|
|
71
|
+
});
|
|
72
|
+
return data;
|
|
73
|
+
} catch (error) {
|
|
74
|
+
const err = error instanceof Error ? error : /* @__PURE__ */ new Error("Unknown error");
|
|
75
|
+
setState({
|
|
76
|
+
data: null,
|
|
77
|
+
status: "error",
|
|
78
|
+
error: err,
|
|
79
|
+
isLoading: false,
|
|
80
|
+
isError: true,
|
|
81
|
+
isSuccess: false
|
|
82
|
+
});
|
|
83
|
+
throw err;
|
|
84
|
+
}
|
|
85
|
+
}, [config]);
|
|
86
|
+
const loadMore = useCallback(async (options = {}) => {
|
|
87
|
+
if (!state.data?.nextCursor) throw new Error("No more pages to load");
|
|
88
|
+
const newData = await list({
|
|
89
|
+
...lastOptions,
|
|
90
|
+
...options,
|
|
91
|
+
cursor: state.data.nextCursor
|
|
92
|
+
});
|
|
93
|
+
setState((prev) => {
|
|
94
|
+
if (!prev.data) return {
|
|
95
|
+
...prev,
|
|
96
|
+
data: newData
|
|
97
|
+
};
|
|
98
|
+
const itemsMap = /* @__PURE__ */ new Map();
|
|
99
|
+
prev.data.items.forEach((item) => {
|
|
100
|
+
itemsMap.set(item.key, item);
|
|
101
|
+
});
|
|
102
|
+
newData.items.forEach((item) => {
|
|
103
|
+
itemsMap.set(item.key, item);
|
|
104
|
+
});
|
|
105
|
+
return {
|
|
106
|
+
...prev,
|
|
107
|
+
data: {
|
|
108
|
+
...newData,
|
|
109
|
+
items: Array.from(itemsMap.values())
|
|
110
|
+
}
|
|
111
|
+
};
|
|
112
|
+
});
|
|
113
|
+
return newData;
|
|
114
|
+
}, [
|
|
115
|
+
state.data,
|
|
116
|
+
list,
|
|
117
|
+
lastOptions
|
|
118
|
+
]);
|
|
119
|
+
const reset = useCallback(() => {
|
|
120
|
+
setState({
|
|
121
|
+
data: null,
|
|
122
|
+
status: "idle",
|
|
123
|
+
error: null,
|
|
124
|
+
isLoading: false,
|
|
125
|
+
isError: false,
|
|
126
|
+
isSuccess: false
|
|
127
|
+
});
|
|
128
|
+
}, []);
|
|
129
|
+
return {
|
|
130
|
+
...state,
|
|
131
|
+
list,
|
|
132
|
+
loadMore,
|
|
133
|
+
reset,
|
|
134
|
+
hasMore: state.data?.hasMore ?? false
|
|
135
|
+
};
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
//#endregion
|
|
139
|
+
//#region src/client/hooks/useDeleteFile.ts
|
|
140
|
+
/**
|
|
141
|
+
* Hook for deleting files
|
|
142
|
+
*/
|
|
143
|
+
function useDeleteFile() {
|
|
144
|
+
const config = useFileManagerConfig();
|
|
145
|
+
const [state, setState] = useState({
|
|
146
|
+
status: "idle",
|
|
147
|
+
error: null,
|
|
148
|
+
isLoading: false,
|
|
149
|
+
isError: false,
|
|
150
|
+
isSuccess: false
|
|
151
|
+
});
|
|
152
|
+
const deleteFile = useCallback(async (key) => {
|
|
153
|
+
setState({
|
|
154
|
+
status: "loading",
|
|
155
|
+
error: null,
|
|
156
|
+
isLoading: true,
|
|
157
|
+
isError: false,
|
|
158
|
+
isSuccess: false
|
|
159
|
+
});
|
|
160
|
+
try {
|
|
161
|
+
const json = await (await (config.fetch ?? fetch)(config.endpoint, {
|
|
162
|
+
method: "POST",
|
|
163
|
+
headers: {
|
|
164
|
+
"Content-Type": "application/json",
|
|
165
|
+
...config.headers
|
|
166
|
+
},
|
|
167
|
+
body: JSON.stringify({
|
|
168
|
+
action: "delete",
|
|
169
|
+
key
|
|
170
|
+
})
|
|
171
|
+
})).json();
|
|
172
|
+
if (!json.success) throw new Error(json.error?.message ?? "Failed to delete file");
|
|
173
|
+
const result = json.data;
|
|
174
|
+
if (!result.success) throw new Error(result.error ?? "Failed to delete file");
|
|
175
|
+
setState({
|
|
176
|
+
status: "success",
|
|
177
|
+
error: null,
|
|
178
|
+
isLoading: false,
|
|
179
|
+
isError: false,
|
|
180
|
+
isSuccess: true
|
|
181
|
+
});
|
|
182
|
+
return result;
|
|
183
|
+
} catch (error) {
|
|
184
|
+
const err = error instanceof Error ? error : /* @__PURE__ */ new Error("Unknown error");
|
|
185
|
+
setState({
|
|
186
|
+
status: "error",
|
|
187
|
+
error: err,
|
|
188
|
+
isLoading: false,
|
|
189
|
+
isError: true,
|
|
190
|
+
isSuccess: false
|
|
191
|
+
});
|
|
192
|
+
throw err;
|
|
193
|
+
}
|
|
194
|
+
}, [config]);
|
|
195
|
+
const deleteBatch = useCallback(async (keys) => {
|
|
196
|
+
setState({
|
|
197
|
+
status: "loading",
|
|
198
|
+
error: null,
|
|
199
|
+
isLoading: true,
|
|
200
|
+
isError: false,
|
|
201
|
+
isSuccess: false
|
|
202
|
+
});
|
|
203
|
+
try {
|
|
204
|
+
const json = await (await (config.fetch ?? fetch)(config.endpoint, {
|
|
205
|
+
method: "POST",
|
|
206
|
+
headers: {
|
|
207
|
+
"Content-Type": "application/json",
|
|
208
|
+
...config.headers
|
|
209
|
+
},
|
|
210
|
+
body: JSON.stringify({
|
|
211
|
+
action: "batchDelete",
|
|
212
|
+
keys
|
|
213
|
+
})
|
|
214
|
+
})).json();
|
|
215
|
+
if (!json.success) throw new Error(json.error?.message ?? "Failed to delete files");
|
|
216
|
+
const result = json.data;
|
|
217
|
+
setState({
|
|
218
|
+
status: "success",
|
|
219
|
+
error: null,
|
|
220
|
+
isLoading: false,
|
|
221
|
+
isError: false,
|
|
222
|
+
isSuccess: true
|
|
223
|
+
});
|
|
224
|
+
return result;
|
|
225
|
+
} catch (error) {
|
|
226
|
+
const err = error instanceof Error ? error : /* @__PURE__ */ new Error("Unknown error");
|
|
227
|
+
setState({
|
|
228
|
+
status: "error",
|
|
229
|
+
error: err,
|
|
230
|
+
isLoading: false,
|
|
231
|
+
isError: true,
|
|
232
|
+
isSuccess: false
|
|
233
|
+
});
|
|
234
|
+
throw err;
|
|
235
|
+
}
|
|
236
|
+
}, [config]);
|
|
237
|
+
const reset = useCallback(() => {
|
|
238
|
+
setState({
|
|
239
|
+
status: "idle",
|
|
240
|
+
error: null,
|
|
241
|
+
isLoading: false,
|
|
242
|
+
isError: false,
|
|
243
|
+
isSuccess: false
|
|
244
|
+
});
|
|
245
|
+
}, []);
|
|
246
|
+
return {
|
|
247
|
+
...state,
|
|
248
|
+
deleteFile,
|
|
249
|
+
deleteBatch,
|
|
250
|
+
reset
|
|
251
|
+
};
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
//#endregion
|
|
255
|
+
//#region src/client/hooks/useDownloadUrl.ts
|
|
256
|
+
/**
|
|
257
|
+
* Hook for generating download URLs
|
|
258
|
+
*/
|
|
259
|
+
function useDownloadUrl() {
|
|
260
|
+
const config = useFileManagerConfig();
|
|
261
|
+
const [state, setState] = useState({
|
|
262
|
+
url: null,
|
|
263
|
+
status: "idle",
|
|
264
|
+
error: null,
|
|
265
|
+
isLoading: false,
|
|
266
|
+
isError: false,
|
|
267
|
+
isSuccess: false
|
|
268
|
+
});
|
|
269
|
+
const getUrl = useCallback(async (key, options = {}) => {
|
|
270
|
+
setState({
|
|
271
|
+
url: null,
|
|
272
|
+
status: "loading",
|
|
273
|
+
error: null,
|
|
274
|
+
isLoading: true,
|
|
275
|
+
isError: false,
|
|
276
|
+
isSuccess: false
|
|
277
|
+
});
|
|
278
|
+
try {
|
|
279
|
+
const json = await (await (config.fetch ?? fetch)(config.endpoint, {
|
|
280
|
+
method: "POST",
|
|
281
|
+
headers: {
|
|
282
|
+
"Content-Type": "application/json",
|
|
283
|
+
...config.headers
|
|
284
|
+
},
|
|
285
|
+
body: JSON.stringify({
|
|
286
|
+
action: "getUrl",
|
|
287
|
+
key,
|
|
288
|
+
...options
|
|
289
|
+
})
|
|
290
|
+
})).json();
|
|
291
|
+
if (!json.success) throw new Error(json.error?.message ?? "Failed to get download URL");
|
|
292
|
+
const url = json.data.url;
|
|
293
|
+
setState({
|
|
294
|
+
url,
|
|
295
|
+
status: "success",
|
|
296
|
+
error: null,
|
|
297
|
+
isLoading: false,
|
|
298
|
+
isError: false,
|
|
299
|
+
isSuccess: true
|
|
300
|
+
});
|
|
301
|
+
return url;
|
|
302
|
+
} catch (error) {
|
|
303
|
+
const err = error instanceof Error ? error : /* @__PURE__ */ new Error("Unknown error");
|
|
304
|
+
setState({
|
|
305
|
+
url: null,
|
|
306
|
+
status: "error",
|
|
307
|
+
error: err,
|
|
308
|
+
isLoading: false,
|
|
309
|
+
isError: true,
|
|
310
|
+
isSuccess: false
|
|
311
|
+
});
|
|
312
|
+
throw err;
|
|
313
|
+
}
|
|
314
|
+
}, [config]);
|
|
315
|
+
const download = useCallback(async (key, options = {}) => {
|
|
316
|
+
const url = await getUrl(key, {
|
|
317
|
+
disposition: "attachment",
|
|
318
|
+
...options
|
|
319
|
+
});
|
|
320
|
+
const link = document.createElement("a");
|
|
321
|
+
link.href = url;
|
|
322
|
+
link.download = options.filename ?? key.split("/").pop() ?? "download";
|
|
323
|
+
document.body.appendChild(link);
|
|
324
|
+
link.click();
|
|
325
|
+
document.body.removeChild(link);
|
|
326
|
+
return url;
|
|
327
|
+
}, [getUrl]);
|
|
328
|
+
const reset = useCallback(() => {
|
|
329
|
+
setState({
|
|
330
|
+
url: null,
|
|
331
|
+
status: "idle",
|
|
332
|
+
error: null,
|
|
333
|
+
isLoading: false,
|
|
334
|
+
isError: false,
|
|
335
|
+
isSuccess: false
|
|
336
|
+
});
|
|
337
|
+
}, []);
|
|
338
|
+
return {
|
|
339
|
+
...state,
|
|
340
|
+
getUrl,
|
|
341
|
+
download,
|
|
342
|
+
reset
|
|
343
|
+
};
|
|
344
|
+
}
|
|
345
|
+
|
|
346
|
+
//#endregion
|
|
347
|
+
export { useFileManagerConfig as a, FileManagerProvider as i, useDeleteFile as n, useListFiles as r, useDownloadUrl as t };
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { };
|
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
import { c as ManagedFile, d as StorageFile, i as DownloadUrlOptions, l as PaginatedResponse, r as DeleteResult, s as ListFilesOptions, t as BatchDeleteResult } from "./types-Brk9aR7c.js";
|
|
2
|
+
import React, { ReactNode } from "react";
|
|
3
|
+
|
|
4
|
+
//#region src/client/types.d.ts
|
|
5
|
+
/**
|
|
6
|
+
* API client configuration
|
|
7
|
+
*/
|
|
8
|
+
type FileManagerClientConfig = {
|
|
9
|
+
/** API endpoint URL */
|
|
10
|
+
endpoint: string;
|
|
11
|
+
/** Custom headers (e.g., authorization) */
|
|
12
|
+
headers?: Record<string, string>;
|
|
13
|
+
/** Custom fetch function */
|
|
14
|
+
fetch?: typeof fetch;
|
|
15
|
+
};
|
|
16
|
+
/**
|
|
17
|
+
* Hook status type
|
|
18
|
+
*/
|
|
19
|
+
type HookStatus = 'idle' | 'loading' | 'success' | 'error';
|
|
20
|
+
/**
|
|
21
|
+
* List files hook state
|
|
22
|
+
*/
|
|
23
|
+
type UseListFilesState<TFile = ManagedFile | StorageFile> = {
|
|
24
|
+
data: PaginatedResponse<TFile> | null;
|
|
25
|
+
status: HookStatus;
|
|
26
|
+
error: Error | null;
|
|
27
|
+
isLoading: boolean;
|
|
28
|
+
isError: boolean;
|
|
29
|
+
isSuccess: boolean;
|
|
30
|
+
};
|
|
31
|
+
/**
|
|
32
|
+
* Delete file hook state
|
|
33
|
+
*/
|
|
34
|
+
type UseDeleteFileState = {
|
|
35
|
+
status: HookStatus;
|
|
36
|
+
error: Error | null;
|
|
37
|
+
isLoading: boolean;
|
|
38
|
+
isError: boolean;
|
|
39
|
+
isSuccess: boolean;
|
|
40
|
+
};
|
|
41
|
+
/**
|
|
42
|
+
* Download URL hook state
|
|
43
|
+
*/
|
|
44
|
+
type UseDownloadUrlState = {
|
|
45
|
+
url: string | null;
|
|
46
|
+
status: HookStatus;
|
|
47
|
+
error: Error | null;
|
|
48
|
+
isLoading: boolean;
|
|
49
|
+
isError: boolean;
|
|
50
|
+
isSuccess: boolean;
|
|
51
|
+
};
|
|
52
|
+
/**
|
|
53
|
+
* API response types
|
|
54
|
+
*/
|
|
55
|
+
type ApiSuccessResponse<T> = {
|
|
56
|
+
success: true;
|
|
57
|
+
data: T;
|
|
58
|
+
};
|
|
59
|
+
type ApiErrorResponse = {
|
|
60
|
+
success: false;
|
|
61
|
+
error: {
|
|
62
|
+
message: string;
|
|
63
|
+
details?: any;
|
|
64
|
+
};
|
|
65
|
+
};
|
|
66
|
+
type ApiResponse<T> = ApiSuccessResponse<T> | ApiErrorResponse;
|
|
67
|
+
//#endregion
|
|
68
|
+
//#region src/client/context.d.ts
|
|
69
|
+
/**
|
|
70
|
+
* Provider for file manager client configuration
|
|
71
|
+
*/
|
|
72
|
+
declare function FileManagerProvider({
|
|
73
|
+
children,
|
|
74
|
+
config
|
|
75
|
+
}: {
|
|
76
|
+
children: ReactNode;
|
|
77
|
+
config: FileManagerClientConfig;
|
|
78
|
+
}): React.JSX.Element;
|
|
79
|
+
/**
|
|
80
|
+
* Hook to get file manager client configuration
|
|
81
|
+
*/
|
|
82
|
+
declare function useFileManagerConfig(): FileManagerClientConfig;
|
|
83
|
+
//#endregion
|
|
84
|
+
//#region src/client/hooks/useListFiles.d.ts
|
|
85
|
+
/**
|
|
86
|
+
* Hook for listing files with pagination
|
|
87
|
+
*/
|
|
88
|
+
declare function useListFiles<TFile extends ManagedFile | StorageFile = ManagedFile>(): {
|
|
89
|
+
list: (options?: ListFilesOptions) => Promise<PaginatedResponse<TFile>>;
|
|
90
|
+
loadMore: (options?: Omit<ListFilesOptions, "cursor">) => Promise<PaginatedResponse<TFile>>;
|
|
91
|
+
reset: () => void;
|
|
92
|
+
hasMore: boolean;
|
|
93
|
+
data: PaginatedResponse<TFile> | null;
|
|
94
|
+
status: HookStatus;
|
|
95
|
+
error: Error | null;
|
|
96
|
+
isLoading: boolean;
|
|
97
|
+
isError: boolean;
|
|
98
|
+
isSuccess: boolean;
|
|
99
|
+
};
|
|
100
|
+
//#endregion
|
|
101
|
+
//#region src/client/hooks/useDeleteFile.d.ts
|
|
102
|
+
/**
|
|
103
|
+
* Hook for deleting files
|
|
104
|
+
*/
|
|
105
|
+
declare function useDeleteFile(): {
|
|
106
|
+
deleteFile: (key: string) => Promise<DeleteResult>;
|
|
107
|
+
deleteBatch: (keys: string[]) => Promise<BatchDeleteResult>;
|
|
108
|
+
reset: () => void;
|
|
109
|
+
status: HookStatus;
|
|
110
|
+
error: Error | null;
|
|
111
|
+
isLoading: boolean;
|
|
112
|
+
isError: boolean;
|
|
113
|
+
isSuccess: boolean;
|
|
114
|
+
};
|
|
115
|
+
//#endregion
|
|
116
|
+
//#region src/client/hooks/useDownloadUrl.d.ts
|
|
117
|
+
/**
|
|
118
|
+
* Hook for generating download URLs
|
|
119
|
+
*/
|
|
120
|
+
declare function useDownloadUrl(): {
|
|
121
|
+
getUrl: (key: string, options?: DownloadUrlOptions) => Promise<string>;
|
|
122
|
+
download: (key: string, options?: DownloadUrlOptions) => Promise<string>;
|
|
123
|
+
reset: () => void;
|
|
124
|
+
url: string | null;
|
|
125
|
+
status: HookStatus;
|
|
126
|
+
error: Error | null;
|
|
127
|
+
isLoading: boolean;
|
|
128
|
+
isError: boolean;
|
|
129
|
+
isSuccess: boolean;
|
|
130
|
+
};
|
|
131
|
+
//#endregion
|
|
132
|
+
export { useFileManagerConfig as a, ApiSuccessResponse as c, UseDeleteFileState as d, UseDownloadUrlState as f, FileManagerProvider as i, FileManagerClientConfig as l, useDeleteFile as n, ApiErrorResponse as o, UseListFilesState as p, useListFiles as r, ApiResponse as s, useDownloadUrl as t, HookStatus as u };
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import { a as FileManagerConfig, c as ManagedFile, d as StorageFile, i as DownloadUrlOptions, l as PaginatedResponse, n as DatabaseCallbacks, o as FileManagerError, r as DeleteResult, s as ListFilesOptions, t as BatchDeleteResult, u as PaginationOptions } from "./types-Brk9aR7c.js";
|
|
2
|
+
import { a as useFileManagerConfig, c as ApiSuccessResponse, d as UseDeleteFileState, f as UseDownloadUrlState, i as FileManagerProvider, l as FileManagerClientConfig, n as useDeleteFile, o as ApiErrorResponse, p as UseListFilesState, r as useListFiles, s as ApiResponse, t as useDownloadUrl, u as HookStatus } from "./index-pUAG6PPj.js";
|
|
3
|
+
import { a as FileManager, i as createFileManagerRouter, n as FileManagerRouter, r as RouteConfig, t as FileManagerRequest } from "./router-CD_RQnlx.js";
|
|
4
|
+
import "./index-CHmsNSsb.js";
|
|
5
|
+
export { ApiErrorResponse, ApiResponse, ApiSuccessResponse, BatchDeleteResult, DatabaseCallbacks, DeleteResult, DownloadUrlOptions, FileManager, FileManagerClientConfig, FileManagerConfig, FileManagerError, FileManagerProvider, FileManagerRequest, FileManagerRouter, HookStatus, ListFilesOptions, ManagedFile, PaginatedResponse, PaginationOptions, RouteConfig, StorageFile, UseDeleteFileState, UseDownloadUrlState, UseListFilesState, createFileManagerRouter, useDeleteFile, useDownloadUrl, useFileManagerConfig, useListFiles };
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import { n as createFileManagerRouter, r as FileManager, t as FileManagerRouter } from "./server-CZrPOP0h.js";
|
|
2
|
+
import { a as useFileManagerConfig, i as FileManagerProvider, n as useDeleteFile, r as useListFiles, t as useDownloadUrl } from "./client-DTkToHgY.js";
|
|
3
|
+
|
|
4
|
+
export { FileManager, FileManagerProvider, FileManagerRouter, createFileManagerRouter, useDeleteFile, useDownloadUrl, useFileManagerConfig, useListFiles };
|