@sinoia/hubdoc-tools 1.3.8 → 1.4.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/dist/commands/export-v2.d.ts +45 -0
- package/dist/commands/export-v2.d.ts.map +1 -0
- package/dist/commands/export-v2.js +369 -0
- package/dist/commands/export-v2.js.map +1 -0
- package/dist/commands/import-v2.d.ts +32 -0
- package/dist/commands/import-v2.d.ts.map +1 -0
- package/dist/commands/import-v2.js +331 -0
- package/dist/commands/import-v2.js.map +1 -0
- package/dist/commands/import.d.ts.map +1 -1
- package/dist/commands/import.js +10 -2
- package/dist/commands/import.js.map +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js.map +1 -1
- package/dist/plugins/alfresco/plugin.json +1 -1
- package/dist/plugins/aws-s3/plugin.json +1 -1
- package/dist/plugins/azure-blob/plugin.json +1 -1
- package/dist/plugins/box/plugin.json +1 -1
- package/dist/plugins/core/plugin.json +1 -1
- package/dist/plugins/nuxeo/plugin.json +1 -1
- package/dist/plugins/o365-mail/index.d.ts +26 -0
- package/dist/plugins/o365-mail/index.d.ts.map +1 -0
- package/dist/plugins/o365-mail/index.js +392 -0
- package/dist/plugins/o365-mail/index.js.map +1 -0
- package/dist/plugins/o365-mail/plugin.json +8 -0
- package/dist/plugins/opentext/plugin.json +1 -1
- package/dist/plugins/sharepoint/plugin.json +1 -1
- package/dist/services/hubdoc-api-v2.d.ts +150 -0
- package/dist/services/hubdoc-api-v2.d.ts.map +1 -0
- package/dist/services/hubdoc-api-v2.js +418 -0
- package/dist/services/hubdoc-api-v2.js.map +1 -0
- package/dist/services/hubdoc-client.d.ts +246 -0
- package/dist/services/hubdoc-client.d.ts.map +1 -0
- package/dist/services/hubdoc-client.js +714 -0
- package/dist/services/hubdoc-client.js.map +1 -0
- package/dist/services/hubdoc-export-service.d.ts +90 -0
- package/dist/services/hubdoc-export-service.d.ts.map +1 -0
- package/dist/services/hubdoc-export-service.js +443 -0
- package/dist/services/hubdoc-export-service.js.map +1 -0
- package/dist/services/hubdoc-export-v2.d.ts +77 -0
- package/dist/services/hubdoc-export-v2.d.ts.map +1 -0
- package/dist/services/hubdoc-export-v2.js +329 -0
- package/dist/services/hubdoc-export-v2.js.map +1 -0
- package/dist/services/hubdoc-import-service.d.ts +64 -0
- package/dist/services/hubdoc-import-service.d.ts.map +1 -0
- package/dist/services/hubdoc-import-service.js +374 -0
- package/dist/services/hubdoc-import-service.js.map +1 -0
- package/dist/test/hubdoc-api-test.d.ts +8 -0
- package/dist/test/hubdoc-api-test.d.ts.map +1 -0
- package/dist/test/hubdoc-api-test.js +152 -0
- package/dist/test/hubdoc-api-test.js.map +1 -0
- package/dist/types/index.d.ts +11 -0
- package/dist/types/index.d.ts.map +1 -1
- package/package.json +1 -1
|
@@ -0,0 +1,714 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.HubDocClient = void 0;
|
|
7
|
+
const axios_1 = __importDefault(require("axios"));
|
|
8
|
+
const form_data_1 = __importDefault(require("form-data"));
|
|
9
|
+
const fs_extra_1 = __importDefault(require("fs-extra"));
|
|
10
|
+
const path_1 = __importDefault(require("path"));
|
|
11
|
+
const chalk_1 = __importDefault(require("chalk"));
|
|
12
|
+
/**
|
|
13
|
+
* Complete HubDoc API Client with workspaces, folders, files, and pagination support
|
|
14
|
+
*/
|
|
15
|
+
class HubDocClient {
|
|
16
|
+
constructor(config) {
|
|
17
|
+
this.config = config;
|
|
18
|
+
const headers = {
|
|
19
|
+
'Accept': 'application/json'
|
|
20
|
+
};
|
|
21
|
+
// Set authentication headers based on auth type
|
|
22
|
+
if (config.authType === 'oauth2' && config.clientId && config.clientSecret) {
|
|
23
|
+
// OAuth2 Client Credentials
|
|
24
|
+
const credentials = Buffer.from(`${config.clientId}:${config.clientSecret}`).toString('base64');
|
|
25
|
+
headers['Authorization'] = `Basic ${credentials}`;
|
|
26
|
+
}
|
|
27
|
+
else if (config.token) {
|
|
28
|
+
// Bearer token
|
|
29
|
+
headers['Authorization'] = `Bearer ${config.token}`;
|
|
30
|
+
}
|
|
31
|
+
this.client = axios_1.default.create({
|
|
32
|
+
baseURL: config.apiUrl,
|
|
33
|
+
headers,
|
|
34
|
+
timeout: 60000
|
|
35
|
+
});
|
|
36
|
+
// Request interceptor for debugging
|
|
37
|
+
this.client.interceptors.request.use(request => {
|
|
38
|
+
console.log(chalk_1.default.gray(`🔗 ${request.method?.toUpperCase()} ${request.url}`));
|
|
39
|
+
return request;
|
|
40
|
+
});
|
|
41
|
+
// Response interceptor for error handling
|
|
42
|
+
this.client.interceptors.response.use(response => response, error => {
|
|
43
|
+
if (error.response) {
|
|
44
|
+
console.error(chalk_1.default.red(`❌ API Error ${error.response.status}: ${error.response.data?.message || error.message}`));
|
|
45
|
+
}
|
|
46
|
+
else {
|
|
47
|
+
console.error(chalk_1.default.red(`❌ Network Error: ${error.message}`));
|
|
48
|
+
}
|
|
49
|
+
return Promise.reject(error);
|
|
50
|
+
});
|
|
51
|
+
}
|
|
52
|
+
/**
|
|
53
|
+
* Test API connection and authentication
|
|
54
|
+
*/
|
|
55
|
+
async testConnection() {
|
|
56
|
+
try {
|
|
57
|
+
const response = await this.client.get('/auth/me');
|
|
58
|
+
console.log(chalk_1.default.green(`✅ Connected as: ${response.data.email || response.data.username || 'User'}`));
|
|
59
|
+
return true;
|
|
60
|
+
}
|
|
61
|
+
catch (error) {
|
|
62
|
+
console.error(chalk_1.default.red('❌ API connection failed:'), error.message);
|
|
63
|
+
return false;
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
/**
|
|
67
|
+
* Login and get JWT token
|
|
68
|
+
*/
|
|
69
|
+
async login(email, password) {
|
|
70
|
+
try {
|
|
71
|
+
const response = await axios_1.default.post(`${this.config.apiUrl}/auth/login`, {
|
|
72
|
+
email,
|
|
73
|
+
password
|
|
74
|
+
});
|
|
75
|
+
const token = response.data.token || response.data.access_token;
|
|
76
|
+
if (!token) {
|
|
77
|
+
throw new Error('No token received from login response');
|
|
78
|
+
}
|
|
79
|
+
// Update client headers with new token
|
|
80
|
+
this.client.defaults.headers['Authorization'] = `Bearer ${token}`;
|
|
81
|
+
console.log(chalk_1.default.green('✅ Login successful'));
|
|
82
|
+
return token;
|
|
83
|
+
}
|
|
84
|
+
catch (error) {
|
|
85
|
+
throw new Error(`Login failed: ${error.response?.data?.message || error.message}`);
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
/**
|
|
89
|
+
* OAuth2 Client Credentials flow
|
|
90
|
+
*/
|
|
91
|
+
async authenticateOAuth2() {
|
|
92
|
+
if (!this.config.clientId || !this.config.clientSecret) {
|
|
93
|
+
throw new Error('OAuth2 credentials not provided');
|
|
94
|
+
}
|
|
95
|
+
try {
|
|
96
|
+
const credentials = Buffer.from(`${this.config.clientId}:${this.config.clientSecret}`).toString('base64');
|
|
97
|
+
const response = await axios_1.default.post(`${this.config.apiUrl}/oauth/token`, {
|
|
98
|
+
grant_type: 'client_credentials'
|
|
99
|
+
}, {
|
|
100
|
+
headers: {
|
|
101
|
+
'Authorization': `Basic ${credentials}`,
|
|
102
|
+
'Content-Type': 'application/x-www-form-urlencoded'
|
|
103
|
+
}
|
|
104
|
+
});
|
|
105
|
+
const token = response.data.access_token;
|
|
106
|
+
if (!token) {
|
|
107
|
+
throw new Error('No access token received from OAuth2 response');
|
|
108
|
+
}
|
|
109
|
+
// Update client headers with new token
|
|
110
|
+
this.client.defaults.headers['Authorization'] = `Bearer ${token}`;
|
|
111
|
+
console.log(chalk_1.default.green('✅ OAuth2 authentication successful'));
|
|
112
|
+
return token;
|
|
113
|
+
}
|
|
114
|
+
catch (error) {
|
|
115
|
+
throw new Error(`OAuth2 authentication failed: ${error.response?.data?.error_description || error.response?.data?.message || error.message}`);
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
// ==================== WORKSPACES API ====================
|
|
119
|
+
/**
|
|
120
|
+
* Get all workspaces with pagination support
|
|
121
|
+
*/
|
|
122
|
+
async getWorkspaces(params) {
|
|
123
|
+
try {
|
|
124
|
+
const queryParams = {};
|
|
125
|
+
if (params?.page)
|
|
126
|
+
queryParams.page = params.page;
|
|
127
|
+
if (params?.limit)
|
|
128
|
+
queryParams.limit = params.limit;
|
|
129
|
+
if (params?.sort)
|
|
130
|
+
queryParams.sort = params.sort;
|
|
131
|
+
if (params?.order)
|
|
132
|
+
queryParams.order = params.order;
|
|
133
|
+
if (params?.domain)
|
|
134
|
+
queryParams.domain = params.domain;
|
|
135
|
+
const response = await this.client.get('/workspaces', { params: queryParams });
|
|
136
|
+
return this.formatPaginatedResponse(response);
|
|
137
|
+
}
|
|
138
|
+
catch (error) {
|
|
139
|
+
throw new Error(`Failed to fetch workspaces: ${error.message}`);
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
/**
|
|
143
|
+
* Get all workspaces (paginated iterator)
|
|
144
|
+
*/
|
|
145
|
+
async getAllWorkspaces(domain) {
|
|
146
|
+
const allWorkspaces = [];
|
|
147
|
+
let currentPage = 1;
|
|
148
|
+
let hasMore = true;
|
|
149
|
+
while (hasMore) {
|
|
150
|
+
const response = await this.getWorkspaces({
|
|
151
|
+
page: currentPage,
|
|
152
|
+
limit: 100,
|
|
153
|
+
domain
|
|
154
|
+
});
|
|
155
|
+
allWorkspaces.push(...response.data);
|
|
156
|
+
hasMore = response.pagination.has_next;
|
|
157
|
+
currentPage++;
|
|
158
|
+
console.log(chalk_1.default.gray(`📄 Loaded ${allWorkspaces.length} workspaces...`));
|
|
159
|
+
}
|
|
160
|
+
return allWorkspaces;
|
|
161
|
+
}
|
|
162
|
+
/**
|
|
163
|
+
* Create a new workspace
|
|
164
|
+
*/
|
|
165
|
+
async createWorkspace(request) {
|
|
166
|
+
try {
|
|
167
|
+
const response = await this.client.post('/workspaces', request);
|
|
168
|
+
console.log(chalk_1.default.blue(`🏢 Created workspace: ${request.name}`));
|
|
169
|
+
return response.data;
|
|
170
|
+
}
|
|
171
|
+
catch (error) {
|
|
172
|
+
throw new Error(`Failed to create workspace "${request.name}": ${error.message}`);
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
/**
|
|
176
|
+
* Get workspace by ID
|
|
177
|
+
*/
|
|
178
|
+
async getWorkspace(id) {
|
|
179
|
+
try {
|
|
180
|
+
const response = await this.client.get(`/workspaces/${id}`);
|
|
181
|
+
return response.data;
|
|
182
|
+
}
|
|
183
|
+
catch (error) {
|
|
184
|
+
throw new Error(`Failed to fetch workspace ${id}: ${error.message}`);
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
/**
|
|
188
|
+
* Find workspace by name and optionally domain
|
|
189
|
+
*/
|
|
190
|
+
async findWorkspace(name, domain) {
|
|
191
|
+
const workspaces = await this.getAllWorkspaces(domain);
|
|
192
|
+
return workspaces.find(w => w.name === name && (!domain || w.domain === domain)) || null;
|
|
193
|
+
}
|
|
194
|
+
/**
|
|
195
|
+
* Find or create workspace
|
|
196
|
+
*/
|
|
197
|
+
async findOrCreateWorkspace(name, domain, permissions) {
|
|
198
|
+
let workspace = await this.findWorkspace(name, domain);
|
|
199
|
+
if (!workspace) {
|
|
200
|
+
const request = {
|
|
201
|
+
name,
|
|
202
|
+
description: `Auto-created workspace for: ${name}`,
|
|
203
|
+
domain
|
|
204
|
+
};
|
|
205
|
+
if (permissions) {
|
|
206
|
+
request.permissions = this.convertPermissions(permissions);
|
|
207
|
+
}
|
|
208
|
+
workspace = await this.createWorkspace(request);
|
|
209
|
+
}
|
|
210
|
+
return workspace;
|
|
211
|
+
}
|
|
212
|
+
// ==================== FOLDERS API ====================
|
|
213
|
+
/**
|
|
214
|
+
* Get folders with pagination support
|
|
215
|
+
*/
|
|
216
|
+
async getFolders(params) {
|
|
217
|
+
try {
|
|
218
|
+
const queryParams = {};
|
|
219
|
+
if (params?.page)
|
|
220
|
+
queryParams.page = params.page;
|
|
221
|
+
if (params?.limit)
|
|
222
|
+
queryParams.limit = params.limit;
|
|
223
|
+
if (params?.sort)
|
|
224
|
+
queryParams.sort = params.sort;
|
|
225
|
+
if (params?.order)
|
|
226
|
+
queryParams.order = params.order;
|
|
227
|
+
if (params?.workspace_id)
|
|
228
|
+
queryParams.workspace_id = params.workspace_id;
|
|
229
|
+
if (params?.parent_id)
|
|
230
|
+
queryParams.parent_id = params.parent_id;
|
|
231
|
+
if (params?.domain)
|
|
232
|
+
queryParams.domain = params.domain;
|
|
233
|
+
const response = await this.client.get('/folders', { params: queryParams });
|
|
234
|
+
return this.formatPaginatedResponse(response);
|
|
235
|
+
}
|
|
236
|
+
catch (error) {
|
|
237
|
+
throw new Error(`Failed to fetch folders: ${error.message}`);
|
|
238
|
+
}
|
|
239
|
+
}
|
|
240
|
+
/**
|
|
241
|
+
* Get all folders (paginated iterator)
|
|
242
|
+
*/
|
|
243
|
+
async getAllFolders(workspaceId, domain) {
|
|
244
|
+
const allFolders = [];
|
|
245
|
+
let currentPage = 1;
|
|
246
|
+
let hasMore = true;
|
|
247
|
+
while (hasMore) {
|
|
248
|
+
const response = await this.getFolders({
|
|
249
|
+
page: currentPage,
|
|
250
|
+
limit: 100,
|
|
251
|
+
workspace_id: workspaceId,
|
|
252
|
+
domain
|
|
253
|
+
});
|
|
254
|
+
allFolders.push(...response.data);
|
|
255
|
+
hasMore = response.pagination.has_next;
|
|
256
|
+
currentPage++;
|
|
257
|
+
console.log(chalk_1.default.gray(`📁 Loaded ${allFolders.length} folders...`));
|
|
258
|
+
}
|
|
259
|
+
return allFolders;
|
|
260
|
+
}
|
|
261
|
+
/**
|
|
262
|
+
* Create a new folder
|
|
263
|
+
*/
|
|
264
|
+
async createFolder(request) {
|
|
265
|
+
try {
|
|
266
|
+
const response = await this.client.post('/folders', request);
|
|
267
|
+
console.log(chalk_1.default.blue(`📁 Created folder: ${request.name}`));
|
|
268
|
+
return response.data;
|
|
269
|
+
}
|
|
270
|
+
catch (error) {
|
|
271
|
+
throw new Error(`Failed to create folder "${request.name}": ${error.message}`);
|
|
272
|
+
}
|
|
273
|
+
}
|
|
274
|
+
/**
|
|
275
|
+
* Get folder by ID
|
|
276
|
+
*/
|
|
277
|
+
async getFolder(id) {
|
|
278
|
+
try {
|
|
279
|
+
const response = await this.client.get(`/folders/${id}`);
|
|
280
|
+
return response.data;
|
|
281
|
+
}
|
|
282
|
+
catch (error) {
|
|
283
|
+
throw new Error(`Failed to fetch folder ${id}: ${error.message}`);
|
|
284
|
+
}
|
|
285
|
+
}
|
|
286
|
+
/**
|
|
287
|
+
* Find or create folder hierarchy
|
|
288
|
+
*/
|
|
289
|
+
async findOrCreateFolderPath(folderPath, workspaceId, domain, permissions) {
|
|
290
|
+
const pathParts = folderPath.split('/').filter(part => part.trim().length > 0);
|
|
291
|
+
let parentId;
|
|
292
|
+
let currentFolder;
|
|
293
|
+
for (const folderName of pathParts) {
|
|
294
|
+
const folders = await this.getAllFolders(workspaceId, domain);
|
|
295
|
+
// Find existing folder
|
|
296
|
+
currentFolder = folders.find(f => f.name === folderName &&
|
|
297
|
+
f.parent_id === parentId &&
|
|
298
|
+
(!workspaceId || f.workspace_id === workspaceId) &&
|
|
299
|
+
(!domain || f.domain === domain));
|
|
300
|
+
if (!currentFolder) {
|
|
301
|
+
// Create new folder
|
|
302
|
+
const request = {
|
|
303
|
+
name: folderName,
|
|
304
|
+
parent_id: parentId,
|
|
305
|
+
workspace_id: workspaceId,
|
|
306
|
+
domain
|
|
307
|
+
};
|
|
308
|
+
if (permissions) {
|
|
309
|
+
request.permissions = this.convertPermissions(permissions);
|
|
310
|
+
}
|
|
311
|
+
currentFolder = await this.createFolder(request);
|
|
312
|
+
}
|
|
313
|
+
parentId = currentFolder.id;
|
|
314
|
+
}
|
|
315
|
+
if (!currentFolder) {
|
|
316
|
+
throw new Error(`Failed to create folder path: ${folderPath}`);
|
|
317
|
+
}
|
|
318
|
+
return currentFolder;
|
|
319
|
+
}
|
|
320
|
+
// ==================== FILES API ====================
|
|
321
|
+
/**
|
|
322
|
+
* Get files with pagination support
|
|
323
|
+
*/
|
|
324
|
+
async getFiles(params) {
|
|
325
|
+
try {
|
|
326
|
+
const queryParams = {};
|
|
327
|
+
if (params?.page)
|
|
328
|
+
queryParams.page = params.page;
|
|
329
|
+
if (params?.limit)
|
|
330
|
+
queryParams.limit = params.limit;
|
|
331
|
+
if (params?.sort)
|
|
332
|
+
queryParams.sort = params.sort;
|
|
333
|
+
if (params?.order)
|
|
334
|
+
queryParams.order = params.order;
|
|
335
|
+
if (params?.folder_id)
|
|
336
|
+
queryParams.folder_id = params.folder_id;
|
|
337
|
+
if (params?.workspace_id)
|
|
338
|
+
queryParams.workspace_id = params.workspace_id;
|
|
339
|
+
if (params?.domain)
|
|
340
|
+
queryParams.domain = params.domain;
|
|
341
|
+
const response = await this.client.get('/files', { params: queryParams });
|
|
342
|
+
return this.formatPaginatedResponse(response);
|
|
343
|
+
}
|
|
344
|
+
catch (error) {
|
|
345
|
+
throw new Error(`Failed to fetch files: ${error.message}`);
|
|
346
|
+
}
|
|
347
|
+
}
|
|
348
|
+
/**
|
|
349
|
+
* Get all files (paginated iterator)
|
|
350
|
+
*/
|
|
351
|
+
async getAllFiles(folderId, workspaceId, domain) {
|
|
352
|
+
const allFiles = [];
|
|
353
|
+
let currentPage = 1;
|
|
354
|
+
let hasMore = true;
|
|
355
|
+
while (hasMore) {
|
|
356
|
+
const response = await this.getFiles({
|
|
357
|
+
page: currentPage,
|
|
358
|
+
limit: 100,
|
|
359
|
+
folder_id: folderId,
|
|
360
|
+
workspace_id: workspaceId,
|
|
361
|
+
domain
|
|
362
|
+
});
|
|
363
|
+
allFiles.push(...response.data);
|
|
364
|
+
hasMore = response.pagination.has_next;
|
|
365
|
+
currentPage++;
|
|
366
|
+
console.log(chalk_1.default.gray(`📄 Loaded ${allFiles.length} files...`));
|
|
367
|
+
}
|
|
368
|
+
return allFiles;
|
|
369
|
+
}
|
|
370
|
+
/**
|
|
371
|
+
* Upload a file
|
|
372
|
+
*/
|
|
373
|
+
async uploadFile(filePath, request) {
|
|
374
|
+
if (!await fs_extra_1.default.pathExists(filePath)) {
|
|
375
|
+
throw new Error(`File not found: ${filePath}`);
|
|
376
|
+
}
|
|
377
|
+
const formData = new form_data_1.default();
|
|
378
|
+
const fileStream = fs_extra_1.default.createReadStream(filePath);
|
|
379
|
+
const fileName = path_1.default.basename(filePath);
|
|
380
|
+
const stats = await fs_extra_1.default.stat(filePath);
|
|
381
|
+
formData.append('file', fileStream, {
|
|
382
|
+
filename: fileName,
|
|
383
|
+
contentType: this.getMimeType(filePath)
|
|
384
|
+
});
|
|
385
|
+
// Add optional parameters
|
|
386
|
+
if (request.folder_id) {
|
|
387
|
+
formData.append('folder_id', request.folder_id);
|
|
388
|
+
}
|
|
389
|
+
if (request.workspace_id) {
|
|
390
|
+
formData.append('workspace_id', request.workspace_id);
|
|
391
|
+
}
|
|
392
|
+
if (request.domain) {
|
|
393
|
+
formData.append('domain', request.domain);
|
|
394
|
+
}
|
|
395
|
+
if (request.metadata) {
|
|
396
|
+
formData.append('metadata', JSON.stringify(request.metadata));
|
|
397
|
+
}
|
|
398
|
+
if (request.permissions) {
|
|
399
|
+
formData.append('permissions', JSON.stringify(request.permissions));
|
|
400
|
+
}
|
|
401
|
+
try {
|
|
402
|
+
const response = await this.client.post('/files', formData, {
|
|
403
|
+
headers: {
|
|
404
|
+
...formData.getHeaders(),
|
|
405
|
+
},
|
|
406
|
+
maxContentLength: Infinity,
|
|
407
|
+
maxBodyLength: Infinity
|
|
408
|
+
});
|
|
409
|
+
const file = response.data;
|
|
410
|
+
console.log(chalk_1.default.green(`✅ Uploaded: ${fileName} (${this.formatFileSize(stats.size)})`));
|
|
411
|
+
return file;
|
|
412
|
+
}
|
|
413
|
+
catch (error) {
|
|
414
|
+
throw new Error(`Failed to upload file "${fileName}": ${error.response?.data?.message || error.message}`);
|
|
415
|
+
}
|
|
416
|
+
}
|
|
417
|
+
/**
|
|
418
|
+
* Get file by ID
|
|
419
|
+
*/
|
|
420
|
+
async getFile(id) {
|
|
421
|
+
try {
|
|
422
|
+
const response = await this.client.get(`/files/${id}`);
|
|
423
|
+
return response.data;
|
|
424
|
+
}
|
|
425
|
+
catch (error) {
|
|
426
|
+
throw new Error(`Failed to fetch file ${id}: ${error.message}`);
|
|
427
|
+
}
|
|
428
|
+
}
|
|
429
|
+
/**
|
|
430
|
+
* Download a file
|
|
431
|
+
*/
|
|
432
|
+
async downloadFile(id, targetPath) {
|
|
433
|
+
try {
|
|
434
|
+
// Create directory if it doesn't exist
|
|
435
|
+
await fs_extra_1.default.ensureDir(path_1.default.dirname(targetPath));
|
|
436
|
+
// Get download URL
|
|
437
|
+
const response = await this.client.get(`/files/${id}/download`, {
|
|
438
|
+
responseType: 'stream'
|
|
439
|
+
});
|
|
440
|
+
// Save to file
|
|
441
|
+
const writer = fs_extra_1.default.createWriteStream(targetPath);
|
|
442
|
+
response.data.pipe(writer);
|
|
443
|
+
return new Promise((resolve, reject) => {
|
|
444
|
+
writer.on('finish', resolve);
|
|
445
|
+
writer.on('error', reject);
|
|
446
|
+
});
|
|
447
|
+
}
|
|
448
|
+
catch (error) {
|
|
449
|
+
throw new Error(`Failed to download file ${id}: ${error.message}`);
|
|
450
|
+
}
|
|
451
|
+
}
|
|
452
|
+
// ==================== EXPORT METHODS ====================
|
|
453
|
+
/**
|
|
454
|
+
* Scan all accessible files and folders for export
|
|
455
|
+
*/
|
|
456
|
+
async scanForExport(domain) {
|
|
457
|
+
console.log(chalk_1.default.blue('🔍 Scanning workspaces, folders, and files...'));
|
|
458
|
+
const exportItems = [];
|
|
459
|
+
// Get all workspaces
|
|
460
|
+
const workspaces = await this.getAllWorkspaces(domain);
|
|
461
|
+
console.log(chalk_1.default.gray(`Found ${workspaces.length} workspaces`));
|
|
462
|
+
for (const workspace of workspaces) {
|
|
463
|
+
// Add workspace to export items
|
|
464
|
+
exportItems.push({
|
|
465
|
+
id: workspace.id,
|
|
466
|
+
name: workspace.name,
|
|
467
|
+
type: 'workspace',
|
|
468
|
+
path: workspace.name,
|
|
469
|
+
metadata: workspace.metadata
|
|
470
|
+
});
|
|
471
|
+
// Get folders in workspace
|
|
472
|
+
const folders = await this.getAllFolders(workspace.id, domain);
|
|
473
|
+
console.log(chalk_1.default.gray(`Found ${folders.length} folders in workspace "${workspace.name}"`));
|
|
474
|
+
// Build folder hierarchy
|
|
475
|
+
const folderMap = new Map();
|
|
476
|
+
folders.forEach(folder => folderMap.set(folder.id, folder));
|
|
477
|
+
for (const folder of folders) {
|
|
478
|
+
const folderPath = this.buildFolderPath(folder, folderMap, workspace.name);
|
|
479
|
+
exportItems.push({
|
|
480
|
+
id: folder.id,
|
|
481
|
+
name: folder.name,
|
|
482
|
+
type: 'folder',
|
|
483
|
+
parent_id: folder.parent_id,
|
|
484
|
+
workspace_id: folder.workspace_id,
|
|
485
|
+
path: folderPath,
|
|
486
|
+
metadata: folder.metadata
|
|
487
|
+
});
|
|
488
|
+
// Get files in folder
|
|
489
|
+
const files = await this.getAllFiles(folder.id, workspace.id, domain);
|
|
490
|
+
console.log(chalk_1.default.gray(`Found ${files.length} files in folder "${folder.name}"`));
|
|
491
|
+
for (const file of files) {
|
|
492
|
+
exportItems.push({
|
|
493
|
+
id: file.id,
|
|
494
|
+
name: file.name,
|
|
495
|
+
type: 'file',
|
|
496
|
+
folder_id: file.folder_id,
|
|
497
|
+
workspace_id: file.workspace_id,
|
|
498
|
+
path: `${folderPath}/${file.name}`,
|
|
499
|
+
size: file.size,
|
|
500
|
+
metadata: file.metadata,
|
|
501
|
+
download_url: `/files/${file.id}/download`
|
|
502
|
+
});
|
|
503
|
+
}
|
|
504
|
+
}
|
|
505
|
+
// Get files directly in workspace (not in folders)
|
|
506
|
+
const directFiles = await this.getAllFiles(undefined, workspace.id, domain);
|
|
507
|
+
console.log(chalk_1.default.gray(`Found ${directFiles.length} files directly in workspace "${workspace.name}"`));
|
|
508
|
+
for (const file of directFiles) {
|
|
509
|
+
if (!file.folder_id) { // Only files not already in folders
|
|
510
|
+
exportItems.push({
|
|
511
|
+
id: file.id,
|
|
512
|
+
name: file.name,
|
|
513
|
+
type: 'file',
|
|
514
|
+
workspace_id: file.workspace_id,
|
|
515
|
+
path: `${workspace.name}/${file.name}`,
|
|
516
|
+
size: file.size,
|
|
517
|
+
metadata: file.metadata,
|
|
518
|
+
download_url: `/files/${file.id}/download`
|
|
519
|
+
});
|
|
520
|
+
}
|
|
521
|
+
}
|
|
522
|
+
}
|
|
523
|
+
// Get files not in any workspace (if any)
|
|
524
|
+
const orphanFiles = await this.getAllFiles(undefined, undefined, domain);
|
|
525
|
+
const workspaceFiles = exportItems.filter(item => item.type === 'file').map(item => item.id);
|
|
526
|
+
for (const file of orphanFiles) {
|
|
527
|
+
if (!workspaceFiles.includes(file.id)) {
|
|
528
|
+
exportItems.push({
|
|
529
|
+
id: file.id,
|
|
530
|
+
name: file.name,
|
|
531
|
+
type: 'file',
|
|
532
|
+
path: `Unorganized/${file.name}`,
|
|
533
|
+
size: file.size,
|
|
534
|
+
metadata: file.metadata,
|
|
535
|
+
download_url: `/files/${file.id}/download`
|
|
536
|
+
});
|
|
537
|
+
}
|
|
538
|
+
}
|
|
539
|
+
console.log(chalk_1.default.green(`✅ Scan complete: ${exportItems.length} items found`));
|
|
540
|
+
return exportItems;
|
|
541
|
+
}
|
|
542
|
+
// ==================== IMPORT METHODS ====================
|
|
543
|
+
/**
|
|
544
|
+
* Import document using mapping
|
|
545
|
+
*/
|
|
546
|
+
async importDocument(mapping) {
|
|
547
|
+
const result = {
|
|
548
|
+
success: false,
|
|
549
|
+
filePath: mapping.filePath
|
|
550
|
+
};
|
|
551
|
+
try {
|
|
552
|
+
let workspaceId;
|
|
553
|
+
let folderId;
|
|
554
|
+
// Handle workspace
|
|
555
|
+
if (mapping.workspace) {
|
|
556
|
+
const workspace = await this.findOrCreateWorkspace(mapping.workspace, mapping.domain, mapping.permissions);
|
|
557
|
+
workspaceId = workspace.id;
|
|
558
|
+
}
|
|
559
|
+
// Handle folder
|
|
560
|
+
if (mapping.targetFolder) {
|
|
561
|
+
const folder = await this.findOrCreateFolderPath(mapping.targetFolder, workspaceId, mapping.domain, mapping.permissions);
|
|
562
|
+
folderId = folder.id;
|
|
563
|
+
}
|
|
564
|
+
// Prepare upload request
|
|
565
|
+
const uploadRequest = {
|
|
566
|
+
folder_id: folderId,
|
|
567
|
+
workspace_id: workspaceId,
|
|
568
|
+
domain: mapping.domain,
|
|
569
|
+
metadata: mapping.metadata
|
|
570
|
+
};
|
|
571
|
+
if (mapping.permissions) {
|
|
572
|
+
uploadRequest.permissions = this.convertPermissions(mapping.permissions);
|
|
573
|
+
}
|
|
574
|
+
// Upload file
|
|
575
|
+
const file = await this.uploadFile(mapping.filePath, uploadRequest);
|
|
576
|
+
result.success = true;
|
|
577
|
+
result.document = file;
|
|
578
|
+
const targetLocation = [
|
|
579
|
+
mapping.workspace,
|
|
580
|
+
mapping.targetFolder,
|
|
581
|
+
path_1.default.basename(mapping.filePath)
|
|
582
|
+
].filter(Boolean).join(' → ');
|
|
583
|
+
console.log(chalk_1.default.green(`✅ Imported: ${targetLocation}`));
|
|
584
|
+
}
|
|
585
|
+
catch (error) {
|
|
586
|
+
result.error = error.message;
|
|
587
|
+
console.error(chalk_1.default.red(`❌ Failed to import ${mapping.filePath}: ${error.message}`));
|
|
588
|
+
}
|
|
589
|
+
return result;
|
|
590
|
+
}
|
|
591
|
+
// ==================== SEARCH API ====================
|
|
592
|
+
/**
|
|
593
|
+
* Search across workspaces, folders, and files
|
|
594
|
+
*/
|
|
595
|
+
async search(query, type, domain) {
|
|
596
|
+
try {
|
|
597
|
+
const params = { q: query };
|
|
598
|
+
if (type)
|
|
599
|
+
params.type = type;
|
|
600
|
+
if (domain)
|
|
601
|
+
params.domain = domain;
|
|
602
|
+
const response = await this.client.get('/search', { params });
|
|
603
|
+
return response.data;
|
|
604
|
+
}
|
|
605
|
+
catch (error) {
|
|
606
|
+
throw new Error(`Search failed: ${error.message}`);
|
|
607
|
+
}
|
|
608
|
+
}
|
|
609
|
+
// ==================== HELPER METHODS ====================
|
|
610
|
+
/**
|
|
611
|
+
* Format paginated response
|
|
612
|
+
*/
|
|
613
|
+
formatPaginatedResponse(response) {
|
|
614
|
+
// Handle different pagination response formats
|
|
615
|
+
if (response.data.data && response.data.pagination) {
|
|
616
|
+
return response.data;
|
|
617
|
+
}
|
|
618
|
+
else if (Array.isArray(response.data)) {
|
|
619
|
+
// Fallback for non-paginated responses
|
|
620
|
+
return {
|
|
621
|
+
data: response.data,
|
|
622
|
+
pagination: {
|
|
623
|
+
current_page: 1,
|
|
624
|
+
total_pages: 1,
|
|
625
|
+
total_items: response.data.length,
|
|
626
|
+
items_per_page: response.data.length,
|
|
627
|
+
has_next: false,
|
|
628
|
+
has_prev: false
|
|
629
|
+
}
|
|
630
|
+
};
|
|
631
|
+
}
|
|
632
|
+
else {
|
|
633
|
+
throw new Error('Unexpected response format');
|
|
634
|
+
}
|
|
635
|
+
}
|
|
636
|
+
/**
|
|
637
|
+
* Build full folder path from hierarchy
|
|
638
|
+
*/
|
|
639
|
+
buildFolderPath(folder, folderMap, workspaceName) {
|
|
640
|
+
const path = [];
|
|
641
|
+
let current = folder;
|
|
642
|
+
while (current) {
|
|
643
|
+
path.unshift(current.name);
|
|
644
|
+
current = current.parent_id ? folderMap.get(current.parent_id) : undefined;
|
|
645
|
+
}
|
|
646
|
+
return `${workspaceName}/${path.join('/')}`;
|
|
647
|
+
}
|
|
648
|
+
/**
|
|
649
|
+
* Convert DocumentMapping permissions to HubDoc API format
|
|
650
|
+
*/
|
|
651
|
+
convertPermissions(permissions) {
|
|
652
|
+
if (!permissions)
|
|
653
|
+
return [];
|
|
654
|
+
const result = [];
|
|
655
|
+
if (permissions.read) {
|
|
656
|
+
result.push({
|
|
657
|
+
level: 'read',
|
|
658
|
+
users: permissions.read.users || [],
|
|
659
|
+
groups: permissions.read.groups || []
|
|
660
|
+
});
|
|
661
|
+
}
|
|
662
|
+
if (permissions.write) {
|
|
663
|
+
result.push({
|
|
664
|
+
level: 'write',
|
|
665
|
+
users: permissions.write.users || [],
|
|
666
|
+
groups: permissions.write.groups || []
|
|
667
|
+
});
|
|
668
|
+
}
|
|
669
|
+
return result;
|
|
670
|
+
}
|
|
671
|
+
/**
|
|
672
|
+
* Get MIME type from file extension
|
|
673
|
+
*/
|
|
674
|
+
getMimeType(filePath) {
|
|
675
|
+
const ext = path_1.default.extname(filePath).toLowerCase();
|
|
676
|
+
const mimeTypes = {
|
|
677
|
+
'.pdf': 'application/pdf',
|
|
678
|
+
'.doc': 'application/msword',
|
|
679
|
+
'.docx': 'application/vnd.openxmlformats-officedocument.wordprocessingml.document',
|
|
680
|
+
'.xls': 'application/vnd.ms-excel',
|
|
681
|
+
'.xlsx': 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',
|
|
682
|
+
'.ppt': 'application/vnd.ms-powerpoint',
|
|
683
|
+
'.pptx': 'application/vnd.openxmlformats-officedocument.presentationml.presentation',
|
|
684
|
+
'.txt': 'text/plain',
|
|
685
|
+
'.csv': 'text/csv',
|
|
686
|
+
'.json': 'application/json',
|
|
687
|
+
'.xml': 'application/xml',
|
|
688
|
+
'.jpg': 'image/jpeg',
|
|
689
|
+
'.jpeg': 'image/jpeg',
|
|
690
|
+
'.png': 'image/png',
|
|
691
|
+
'.gif': 'image/gif',
|
|
692
|
+
'.bmp': 'image/bmp',
|
|
693
|
+
'.tiff': 'image/tiff',
|
|
694
|
+
'.zip': 'application/zip',
|
|
695
|
+
'.rar': 'application/vnd.rar'
|
|
696
|
+
};
|
|
697
|
+
return mimeTypes[ext] || 'application/octet-stream';
|
|
698
|
+
}
|
|
699
|
+
/**
|
|
700
|
+
* Format file size for display
|
|
701
|
+
*/
|
|
702
|
+
formatFileSize(bytes) {
|
|
703
|
+
const units = ['B', 'KB', 'MB', 'GB'];
|
|
704
|
+
let size = bytes;
|
|
705
|
+
let unitIndex = 0;
|
|
706
|
+
while (size >= 1024 && unitIndex < units.length - 1) {
|
|
707
|
+
size /= 1024;
|
|
708
|
+
unitIndex++;
|
|
709
|
+
}
|
|
710
|
+
return `${size.toFixed(1)} ${units[unitIndex]}`;
|
|
711
|
+
}
|
|
712
|
+
}
|
|
713
|
+
exports.HubDocClient = HubDocClient;
|
|
714
|
+
//# sourceMappingURL=hubdoc-client.js.map
|