@sinoia/hubdoc-tools 1.3.5 → 1.3.7
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/package.json +3 -4
- package/plugins/alfresco/index.ts +0 -518
- package/plugins/alfresco/plugin.json +0 -12
- package/plugins/aws-s3/index.ts +0 -471
- package/plugins/aws-s3/plugin.json +0 -12
- package/plugins/azure-blob/index.ts +0 -420
- package/plugins/azure-blob/plugin.json +0 -12
- package/plugins/box/index.ts +0 -495
- package/plugins/box/plugin.json +0 -12
- package/plugins/core/README.md +0 -122
- package/plugins/core/TESTING.md +0 -155
- package/plugins/core/index.ts +0 -510
- package/plugins/core/plugin.json +0 -26
- package/plugins/dropbox/index.ts +0 -451
- package/plugins/dropbox/plugin.json +0 -12
- package/plugins/filesystem/index.ts +0 -360
- package/plugins/filesystem/plugin.json +0 -12
- package/plugins/googledrive/index.ts +0 -463
- package/plugins/googledrive/plugin.json +0 -12
- package/plugins/nuxeo/index.ts +0 -512
- package/plugins/nuxeo/plugin.json +0 -12
- package/plugins/onedrive/TESTING.md +0 -197
- package/plugins/onedrive/index.ts +0 -447
- package/plugins/onedrive/plugin.json +0 -12
- package/plugins/opentext/index.ts +0 -542
- package/plugins/opentext/plugin.json +0 -12
- package/plugins/sharepoint/index.ts +0 -509
- package/plugins/sharepoint/plugin.json +0 -12
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@sinoia/hubdoc-tools",
|
|
3
|
-
"version": "1.3.
|
|
3
|
+
"version": "1.3.7",
|
|
4
4
|
"description": "Professional command-line tool for HubDoc document management and bulk import/export",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"types": "dist/index.d.ts",
|
|
@@ -9,7 +9,6 @@
|
|
|
9
9
|
},
|
|
10
10
|
"files": [
|
|
11
11
|
"dist",
|
|
12
|
-
"plugins",
|
|
13
12
|
"README.md",
|
|
14
13
|
"LICENSE"
|
|
15
14
|
],
|
|
@@ -28,8 +27,8 @@
|
|
|
28
27
|
},
|
|
29
28
|
"scripts": {
|
|
30
29
|
"build": "npm run build:main && npm run build:plugins",
|
|
31
|
-
"build:main": "tsc --project tsconfig.main.json && tsc-alias",
|
|
32
|
-
"build:plugins": "tsc --project tsconfig.plugins.json && npm run copy:plugin-manifests",
|
|
30
|
+
"build:main": "tsc --project tsconfig.main.json && tsc-alias -p tsconfig.main.json",
|
|
31
|
+
"build:plugins": "tsc --project tsconfig.plugins.json && tsc-alias -p tsconfig.plugins.json && npm run copy:plugin-manifests",
|
|
33
32
|
"copy:plugin-manifests": "for dir in plugins/*/; do name=$(basename \"$dir\"); [ -f \"$dir/plugin.json\" ] && mkdir -p \"dist/plugins/$name\" && cp \"$dir/plugin.json\" \"dist/plugins/$name/\" || true; done",
|
|
34
33
|
"dev": "ts-node -r tsconfig-paths/register src/cli.ts",
|
|
35
34
|
"start": "node dist/cli.js",
|
|
@@ -1,518 +0,0 @@
|
|
|
1
|
-
import axios, { AxiosInstance } from 'axios';
|
|
2
|
-
import fs from 'fs-extra';
|
|
3
|
-
import path from 'path';
|
|
4
|
-
import {
|
|
5
|
-
DocumentSourcePlugin,
|
|
6
|
-
DocumentSource,
|
|
7
|
-
PluginConfig,
|
|
8
|
-
ScanResult,
|
|
9
|
-
PluginImportOptions,
|
|
10
|
-
PluginExportOptions,
|
|
11
|
-
ImportResult,
|
|
12
|
-
ExportResult
|
|
13
|
-
} from '../../src/types/plugins';
|
|
14
|
-
|
|
15
|
-
interface AlfrescoConfig extends PluginConfig {
|
|
16
|
-
baseUrl: string;
|
|
17
|
-
username: string;
|
|
18
|
-
password: string;
|
|
19
|
-
siteId?: string;
|
|
20
|
-
folderId?: string; // Starting folder ID, defaults to root
|
|
21
|
-
includeFolders?: boolean;
|
|
22
|
-
limit?: number;
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
interface AlfrescoNode {
|
|
26
|
-
id: string;
|
|
27
|
-
name: string;
|
|
28
|
-
nodeType: string;
|
|
29
|
-
isFolder: boolean;
|
|
30
|
-
isFile: boolean;
|
|
31
|
-
createdAt: string;
|
|
32
|
-
modifiedAt: string;
|
|
33
|
-
createdByUser: {
|
|
34
|
-
id: string;
|
|
35
|
-
displayName: string;
|
|
36
|
-
};
|
|
37
|
-
modifiedByUser: {
|
|
38
|
-
id: string;
|
|
39
|
-
displayName: string;
|
|
40
|
-
};
|
|
41
|
-
content?: {
|
|
42
|
-
mimeType: string;
|
|
43
|
-
sizeInBytes: number;
|
|
44
|
-
};
|
|
45
|
-
properties?: Record<string, any>;
|
|
46
|
-
path?: {
|
|
47
|
-
name: string;
|
|
48
|
-
isComplete: boolean;
|
|
49
|
-
elements: Array<{
|
|
50
|
-
id: string;
|
|
51
|
-
name: string;
|
|
52
|
-
}>;
|
|
53
|
-
};
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
export default class AlfrescoPlugin implements DocumentSourcePlugin {
|
|
57
|
-
readonly name = 'alfresco';
|
|
58
|
-
readonly version = '1.0.0';
|
|
59
|
-
readonly description = 'Alfresco ECM document source';
|
|
60
|
-
readonly supportedOperations = ['import', 'export', 'both'] as const;
|
|
61
|
-
|
|
62
|
-
private config?: AlfrescoConfig;
|
|
63
|
-
private apiClient?: AxiosInstance;
|
|
64
|
-
private readonly apiVersion = 'alfresco/versions/1';
|
|
65
|
-
|
|
66
|
-
async testConnection(config: PluginConfig): Promise<boolean> {
|
|
67
|
-
try {
|
|
68
|
-
const client = this.createApiClient(config as AlfrescoConfig);
|
|
69
|
-
const response = await client.get('/people/-me-');
|
|
70
|
-
return response.status === 200;
|
|
71
|
-
} catch (error: any) {
|
|
72
|
-
console.error(`Alfresco connection test failed: ${error.message}`);
|
|
73
|
-
return false;
|
|
74
|
-
}
|
|
75
|
-
}
|
|
76
|
-
|
|
77
|
-
async scan(config: PluginConfig, options?: PluginImportOptions): Promise<ScanResult> {
|
|
78
|
-
this.config = config as AlfrescoConfig;
|
|
79
|
-
this.apiClient = this.createApiClient(this.config);
|
|
80
|
-
|
|
81
|
-
const sources: DocumentSource[] = [];
|
|
82
|
-
const errors: string[] = [];
|
|
83
|
-
let totalSize = 0;
|
|
84
|
-
|
|
85
|
-
try {
|
|
86
|
-
const limit = (this.config as any).limit || (options as any)?.limit;
|
|
87
|
-
console.log(`🔍 Scanning Alfresco repository${limit ? ` (limit: ${limit})` : ''}...`);
|
|
88
|
-
|
|
89
|
-
// Determine starting node ID
|
|
90
|
-
let startingNodeId = this.config.folderId || '-root-';
|
|
91
|
-
|
|
92
|
-
// If siteId is provided, get the document library
|
|
93
|
-
if (this.config.siteId) {
|
|
94
|
-
startingNodeId = await this.getSiteDocumentLibraryId(this.config.siteId);
|
|
95
|
-
}
|
|
96
|
-
|
|
97
|
-
const nodes = await this.scanNode(startingNodeId);
|
|
98
|
-
|
|
99
|
-
let processedCount = 0;
|
|
100
|
-
for (const node of nodes) {
|
|
101
|
-
if (node.isFile && node.content) {
|
|
102
|
-
const source: DocumentSource = {
|
|
103
|
-
id: node.id,
|
|
104
|
-
name: node.name,
|
|
105
|
-
path: this.getNodePath(node),
|
|
106
|
-
size: node.content.sizeInBytes,
|
|
107
|
-
mimeType: node.content.mimeType,
|
|
108
|
-
lastModified: new Date(node.modifiedAt),
|
|
109
|
-
metadata: {
|
|
110
|
-
alfrescoId: node.id,
|
|
111
|
-
nodeType: node.nodeType,
|
|
112
|
-
createdAt: node.createdAt,
|
|
113
|
-
createdBy: node.createdByUser.displayName,
|
|
114
|
-
modifiedBy: node.modifiedByUser.displayName,
|
|
115
|
-
properties: node.properties
|
|
116
|
-
}
|
|
117
|
-
};
|
|
118
|
-
|
|
119
|
-
// Apply filters
|
|
120
|
-
if (this.shouldIncludeSource(source, options)) {
|
|
121
|
-
sources.push(source);
|
|
122
|
-
totalSize += source.size;
|
|
123
|
-
processedCount++;
|
|
124
|
-
|
|
125
|
-
// Check limit
|
|
126
|
-
if (limit && processedCount >= limit) {
|
|
127
|
-
console.log(`📏 Reached limit of ${limit} files`);
|
|
128
|
-
break;
|
|
129
|
-
}
|
|
130
|
-
}
|
|
131
|
-
}
|
|
132
|
-
}
|
|
133
|
-
|
|
134
|
-
return {
|
|
135
|
-
sources,
|
|
136
|
-
totalCount: sources.length,
|
|
137
|
-
totalSize,
|
|
138
|
-
errors
|
|
139
|
-
};
|
|
140
|
-
} catch (error: any) {
|
|
141
|
-
return {
|
|
142
|
-
sources: [],
|
|
143
|
-
totalCount: 0,
|
|
144
|
-
totalSize: 0,
|
|
145
|
-
errors: [`Alfresco scan failed: ${error.message}`]
|
|
146
|
-
};
|
|
147
|
-
}
|
|
148
|
-
}
|
|
149
|
-
|
|
150
|
-
private async scanNode(nodeId: string, parentPath: string = ''): Promise<AlfrescoNode[]> {
|
|
151
|
-
if (!this.apiClient) throw new Error('API client not initialized');
|
|
152
|
-
|
|
153
|
-
const allNodes: AlfrescoNode[] = [];
|
|
154
|
-
let skipCount = 0;
|
|
155
|
-
const maxItems = 100;
|
|
156
|
-
|
|
157
|
-
try {
|
|
158
|
-
while (true) {
|
|
159
|
-
const response = await this.apiClient.get(`/nodes/${nodeId}/children`, {
|
|
160
|
-
params: {
|
|
161
|
-
skipCount,
|
|
162
|
-
maxItems,
|
|
163
|
-
include: 'properties,path',
|
|
164
|
-
fields: 'id,name,nodeType,isFolder,isFile,createdAt,modifiedAt,createdByUser,modifiedByUser,content,properties,path'
|
|
165
|
-
}
|
|
166
|
-
});
|
|
167
|
-
|
|
168
|
-
const nodes: AlfrescoNode[] = response.data.list.entries.map((entry: any) => {
|
|
169
|
-
const node = entry.entry;
|
|
170
|
-
// Add full path information
|
|
171
|
-
(node as any).fullPath = parentPath ? `${parentPath}/${node.name}` : node.name;
|
|
172
|
-
return node;
|
|
173
|
-
});
|
|
174
|
-
|
|
175
|
-
// Process files immediately
|
|
176
|
-
const files = nodes.filter(node => node.isFile);
|
|
177
|
-
allNodes.push(...files);
|
|
178
|
-
|
|
179
|
-
// Recursively process folders if includeFolders is enabled or not specified
|
|
180
|
-
if (this.config?.includeFolders !== false) {
|
|
181
|
-
const folders = nodes.filter(node => node.isFolder);
|
|
182
|
-
for (const folder of folders) {
|
|
183
|
-
const folderPath = parentPath ? `${parentPath}/${folder.name}` : folder.name;
|
|
184
|
-
const subNodes = await this.scanNode(folder.id, folderPath);
|
|
185
|
-
allNodes.push(...subNodes);
|
|
186
|
-
}
|
|
187
|
-
}
|
|
188
|
-
|
|
189
|
-
// Check if there are more items
|
|
190
|
-
if (nodes.length < maxItems) {
|
|
191
|
-
break;
|
|
192
|
-
}
|
|
193
|
-
skipCount += maxItems;
|
|
194
|
-
}
|
|
195
|
-
|
|
196
|
-
return allNodes;
|
|
197
|
-
} catch (error: any) {
|
|
198
|
-
if (error.response?.status === 404) {
|
|
199
|
-
console.warn(`Warning: Node not found: ${nodeId}`);
|
|
200
|
-
return [];
|
|
201
|
-
}
|
|
202
|
-
throw error;
|
|
203
|
-
}
|
|
204
|
-
}
|
|
205
|
-
|
|
206
|
-
async import(
|
|
207
|
-
config: PluginConfig,
|
|
208
|
-
sources: DocumentSource[],
|
|
209
|
-
targetDir: string,
|
|
210
|
-
options?: PluginImportOptions
|
|
211
|
-
): Promise<ImportResult[]> {
|
|
212
|
-
this.config = config as AlfrescoConfig;
|
|
213
|
-
this.apiClient = this.createApiClient(this.config);
|
|
214
|
-
|
|
215
|
-
const results: ImportResult[] = [];
|
|
216
|
-
const batchSize = options?.batchSize || 5;
|
|
217
|
-
|
|
218
|
-
// Process in batches to respect API limits
|
|
219
|
-
for (let i = 0; i < sources.length; i += batchSize) {
|
|
220
|
-
const batch = sources.slice(i, i + batchSize);
|
|
221
|
-
|
|
222
|
-
for (const source of batch) {
|
|
223
|
-
const result = await this.importSingle(source, targetDir);
|
|
224
|
-
results.push(result);
|
|
225
|
-
|
|
226
|
-
// Small delay to respect rate limits
|
|
227
|
-
await this.sleep(200);
|
|
228
|
-
}
|
|
229
|
-
}
|
|
230
|
-
|
|
231
|
-
return results;
|
|
232
|
-
}
|
|
233
|
-
|
|
234
|
-
private async importSingle(source: DocumentSource, targetDir: string): Promise<ImportResult> {
|
|
235
|
-
try {
|
|
236
|
-
if (!this.apiClient) throw new Error('API client not initialized');
|
|
237
|
-
|
|
238
|
-
const targetPath = path.join(targetDir, source.path);
|
|
239
|
-
const targetDirectory = path.dirname(targetPath);
|
|
240
|
-
|
|
241
|
-
await fs.ensureDir(targetDirectory);
|
|
242
|
-
|
|
243
|
-
// Download file content from Alfresco
|
|
244
|
-
const response = await this.apiClient.get(`/nodes/${source.id}/content`, {
|
|
245
|
-
responseType: 'stream'
|
|
246
|
-
});
|
|
247
|
-
|
|
248
|
-
const writer = fs.createWriteStream(targetPath);
|
|
249
|
-
response.data.pipe(writer);
|
|
250
|
-
|
|
251
|
-
return new Promise((resolve) => {
|
|
252
|
-
writer.on('finish', () => {
|
|
253
|
-
resolve({
|
|
254
|
-
success: true,
|
|
255
|
-
source,
|
|
256
|
-
localPath: targetPath,
|
|
257
|
-
bytesTransferred: source.size
|
|
258
|
-
});
|
|
259
|
-
});
|
|
260
|
-
|
|
261
|
-
writer.on('error', (error) => {
|
|
262
|
-
resolve({
|
|
263
|
-
success: false,
|
|
264
|
-
source,
|
|
265
|
-
error: error.message
|
|
266
|
-
});
|
|
267
|
-
});
|
|
268
|
-
});
|
|
269
|
-
} catch (error: any) {
|
|
270
|
-
return {
|
|
271
|
-
success: false,
|
|
272
|
-
source,
|
|
273
|
-
error: error.message
|
|
274
|
-
};
|
|
275
|
-
}
|
|
276
|
-
}
|
|
277
|
-
|
|
278
|
-
async export?(
|
|
279
|
-
config: PluginConfig,
|
|
280
|
-
localSources: DocumentSource[],
|
|
281
|
-
options?: PluginExportOptions
|
|
282
|
-
): Promise<ExportResult[]> {
|
|
283
|
-
this.config = config as AlfrescoConfig;
|
|
284
|
-
this.apiClient = this.createApiClient(this.config);
|
|
285
|
-
|
|
286
|
-
const results: ExportResult[] = [];
|
|
287
|
-
|
|
288
|
-
// Determine target folder ID
|
|
289
|
-
let targetFolderId = this.config.folderId || '-root-';
|
|
290
|
-
if (this.config.siteId) {
|
|
291
|
-
targetFolderId = await this.getSiteDocumentLibraryId(this.config.siteId);
|
|
292
|
-
}
|
|
293
|
-
|
|
294
|
-
for (const source of localSources) {
|
|
295
|
-
try {
|
|
296
|
-
// Determine target folder for this document
|
|
297
|
-
let actualTargetFolderId = targetFolderId;
|
|
298
|
-
|
|
299
|
-
if (options?.preserveStructure && source.path.includes('/')) {
|
|
300
|
-
const folderPath = path.dirname(source.path);
|
|
301
|
-
actualTargetFolderId = await this.createFolderStructure(folderPath, targetFolderId);
|
|
302
|
-
}
|
|
303
|
-
|
|
304
|
-
// Read local file
|
|
305
|
-
const fileContent = await fs.readFile(source.id);
|
|
306
|
-
// Normalize filename to NFC to handle accented characters consistently across platforms
|
|
307
|
-
const fileName = (options?.preserveStructure ? path.basename(source.path) : source.name).normalize('NFC');
|
|
308
|
-
|
|
309
|
-
// Upload file to Alfresco
|
|
310
|
-
const FormData = require('form-data');
|
|
311
|
-
const form = new FormData();
|
|
312
|
-
|
|
313
|
-
form.append('filedata', fileContent, fileName);
|
|
314
|
-
form.append('name', fileName);
|
|
315
|
-
form.append('nodeType', 'cm:content');
|
|
316
|
-
|
|
317
|
-
const response = await this.apiClient!.post(`/nodes/${actualTargetFolderId}/children`, form, {
|
|
318
|
-
headers: {
|
|
319
|
-
...form.getHeaders()
|
|
320
|
-
}
|
|
321
|
-
});
|
|
322
|
-
|
|
323
|
-
const targetPath = options?.preserveStructure ? source.path : source.name;
|
|
324
|
-
|
|
325
|
-
results.push({
|
|
326
|
-
success: true,
|
|
327
|
-
targetPath,
|
|
328
|
-
source,
|
|
329
|
-
bytesTransferred: source.size
|
|
330
|
-
});
|
|
331
|
-
} catch (error: any) {
|
|
332
|
-
results.push({
|
|
333
|
-
success: false,
|
|
334
|
-
targetPath: options?.targetPath || '',
|
|
335
|
-
source,
|
|
336
|
-
error: error.message
|
|
337
|
-
});
|
|
338
|
-
}
|
|
339
|
-
}
|
|
340
|
-
|
|
341
|
-
return results;
|
|
342
|
-
}
|
|
343
|
-
|
|
344
|
-
private async getSiteDocumentLibraryId(siteId: string): Promise<string> {
|
|
345
|
-
if (!this.apiClient) throw new Error('API client not initialized');
|
|
346
|
-
|
|
347
|
-
try {
|
|
348
|
-
const response = await this.apiClient.get(`/sites/${siteId}/containers`);
|
|
349
|
-
const containers = response.data.list.entries;
|
|
350
|
-
|
|
351
|
-
const documentLibrary = containers.find((container: any) =>
|
|
352
|
-
container.entry.folderId === 'documentLibrary'
|
|
353
|
-
);
|
|
354
|
-
|
|
355
|
-
if (!documentLibrary) {
|
|
356
|
-
throw new Error(`Document library not found for site: ${siteId}`);
|
|
357
|
-
}
|
|
358
|
-
|
|
359
|
-
return documentLibrary.entry.id;
|
|
360
|
-
} catch (error: any) {
|
|
361
|
-
throw new Error(`Failed to get document library for site ${siteId}: ${error.message}`);
|
|
362
|
-
}
|
|
363
|
-
}
|
|
364
|
-
|
|
365
|
-
private async createFolderStructure(folderPath: string, parentId: string): Promise<string> {
|
|
366
|
-
if (!this.apiClient) throw new Error('API client not initialized');
|
|
367
|
-
|
|
368
|
-
const parts = folderPath.split('/').filter(part => part.length > 0);
|
|
369
|
-
let currentParentId = parentId;
|
|
370
|
-
|
|
371
|
-
for (const folderName of parts) {
|
|
372
|
-
// Check if folder already exists
|
|
373
|
-
try {
|
|
374
|
-
const response = await this.apiClient.get(`/nodes/${currentParentId}/children`, {
|
|
375
|
-
params: {
|
|
376
|
-
where: `(name='${folderName}' AND nodeType='cm:folder')`,
|
|
377
|
-
maxItems: 1
|
|
378
|
-
}
|
|
379
|
-
});
|
|
380
|
-
|
|
381
|
-
const existingFolder = response.data.list.entries[0];
|
|
382
|
-
|
|
383
|
-
if (existingFolder) {
|
|
384
|
-
currentParentId = existingFolder.entry.id;
|
|
385
|
-
} else {
|
|
386
|
-
// Create new folder
|
|
387
|
-
const createResponse = await this.apiClient.post(`/nodes/${currentParentId}/children`, {
|
|
388
|
-
name: folderName,
|
|
389
|
-
nodeType: 'cm:folder'
|
|
390
|
-
});
|
|
391
|
-
currentParentId = createResponse.data.entry.id;
|
|
392
|
-
}
|
|
393
|
-
} catch (error: any) {
|
|
394
|
-
throw new Error(`Failed to create folder structure: ${error.message}`);
|
|
395
|
-
}
|
|
396
|
-
}
|
|
397
|
-
|
|
398
|
-
return currentParentId;
|
|
399
|
-
}
|
|
400
|
-
|
|
401
|
-
getConfigSchema(): Record<string, any> {
|
|
402
|
-
return {
|
|
403
|
-
type: 'object',
|
|
404
|
-
properties: {
|
|
405
|
-
baseUrl: {
|
|
406
|
-
type: 'string',
|
|
407
|
-
description: 'Alfresco base URL (e.g., http://localhost:8080/alfresco)',
|
|
408
|
-
required: true
|
|
409
|
-
},
|
|
410
|
-
username: {
|
|
411
|
-
type: 'string',
|
|
412
|
-
description: 'Alfresco username',
|
|
413
|
-
required: true
|
|
414
|
-
},
|
|
415
|
-
password: {
|
|
416
|
-
type: 'string',
|
|
417
|
-
description: 'Alfresco password',
|
|
418
|
-
required: true
|
|
419
|
-
},
|
|
420
|
-
siteId: {
|
|
421
|
-
type: 'string',
|
|
422
|
-
description: 'Alfresco site ID to scan (optional, uses repository root if not specified)',
|
|
423
|
-
required: false
|
|
424
|
-
},
|
|
425
|
-
folderId: {
|
|
426
|
-
type: 'string',
|
|
427
|
-
description: 'Starting folder ID (optional, uses site document library or root)',
|
|
428
|
-
required: false
|
|
429
|
-
},
|
|
430
|
-
includeFolders: {
|
|
431
|
-
type: 'boolean',
|
|
432
|
-
description: 'Include subfolders in scan (default: true)',
|
|
433
|
-
default: true
|
|
434
|
-
},
|
|
435
|
-
limit: {
|
|
436
|
-
type: 'number',
|
|
437
|
-
description: 'Maximum number of documents to scan (useful for testing)',
|
|
438
|
-
required: false
|
|
439
|
-
}
|
|
440
|
-
},
|
|
441
|
-
required: ['baseUrl', 'username', 'password']
|
|
442
|
-
};
|
|
443
|
-
}
|
|
444
|
-
|
|
445
|
-
async initialize(config: PluginConfig): Promise<void> {
|
|
446
|
-
this.config = config as AlfrescoConfig;
|
|
447
|
-
|
|
448
|
-
if (!this.config.baseUrl || !this.config.username || !this.config.password) {
|
|
449
|
-
throw new Error('Alfresco base URL, username, and password are required');
|
|
450
|
-
}
|
|
451
|
-
|
|
452
|
-
this.apiClient = this.createApiClient(this.config);
|
|
453
|
-
}
|
|
454
|
-
|
|
455
|
-
async destroy(): Promise<void> {
|
|
456
|
-
this.config = undefined;
|
|
457
|
-
this.apiClient = undefined;
|
|
458
|
-
}
|
|
459
|
-
|
|
460
|
-
private createApiClient(config: AlfrescoConfig): AxiosInstance {
|
|
461
|
-
const baseURL = `${config.baseUrl.replace(/\/$/, '')}/api/-default-/public/${this.apiVersion}`;
|
|
462
|
-
|
|
463
|
-
return axios.create({
|
|
464
|
-
baseURL,
|
|
465
|
-
auth: {
|
|
466
|
-
username: config.username,
|
|
467
|
-
password: config.password
|
|
468
|
-
},
|
|
469
|
-
headers: {
|
|
470
|
-
'Content-Type': 'application/json'
|
|
471
|
-
},
|
|
472
|
-
timeout: 30000
|
|
473
|
-
});
|
|
474
|
-
}
|
|
475
|
-
|
|
476
|
-
private getNodePath(node: AlfrescoNode): string {
|
|
477
|
-
// Use the full path if available from scanning
|
|
478
|
-
if ((node as any).fullPath) {
|
|
479
|
-
return (node as any).fullPath;
|
|
480
|
-
}
|
|
481
|
-
|
|
482
|
-
// Construct path from path collection
|
|
483
|
-
if (node.path && node.path.isComplete) {
|
|
484
|
-
const pathParts = node.path.elements
|
|
485
|
-
.filter(element => element.name !== 'Company Home') // Skip root element
|
|
486
|
-
.map(element => element.name);
|
|
487
|
-
pathParts.push(node.name);
|
|
488
|
-
return pathParts.join('/');
|
|
489
|
-
}
|
|
490
|
-
|
|
491
|
-
return node.name;
|
|
492
|
-
}
|
|
493
|
-
|
|
494
|
-
private shouldIncludeSource(source: DocumentSource, options?: PluginImportOptions): boolean {
|
|
495
|
-
// Apply size filter
|
|
496
|
-
if (options?.filters?.maxSize && source.size > options.filters.maxSize) {
|
|
497
|
-
return false;
|
|
498
|
-
}
|
|
499
|
-
|
|
500
|
-
// Apply date range filter
|
|
501
|
-
if (options?.filters?.dateRange) {
|
|
502
|
-
const { from, to } = options.filters.dateRange;
|
|
503
|
-
if (from && source.lastModified < from) return false;
|
|
504
|
-
if (to && source.lastModified > to) return false;
|
|
505
|
-
}
|
|
506
|
-
|
|
507
|
-
// Apply MIME type filter
|
|
508
|
-
if (options?.filters?.mimeTypes && !options.filters.mimeTypes.includes(source.mimeType)) {
|
|
509
|
-
return false;
|
|
510
|
-
}
|
|
511
|
-
|
|
512
|
-
return true;
|
|
513
|
-
}
|
|
514
|
-
|
|
515
|
-
private sleep(ms: number): Promise<void> {
|
|
516
|
-
return new Promise(resolve => setTimeout(resolve, ms));
|
|
517
|
-
}
|
|
518
|
-
}
|