@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
|
@@ -1,197 +0,0 @@
|
|
|
1
|
-
# Guide de test du plugin OneDrive
|
|
2
|
-
|
|
3
|
-
Ce guide vous permet de tester le plugin OneDrive avec Microsoft Graph API.
|
|
4
|
-
|
|
5
|
-
## Prérequis
|
|
6
|
-
|
|
7
|
-
1. **Azure CLI installé** : `brew install azure-cli` (macOS) ou voir [docs Azure](https://docs.microsoft.com/en-us/cli/azure/install-azure-cli)
|
|
8
|
-
2. **Compte Microsoft/Office 365** avec accès OneDrive
|
|
9
|
-
3. **HubDoc Tools configuré**
|
|
10
|
-
|
|
11
|
-
> ⚠️ **Note** : Pour un test rapide, seul Azure CLI est nécessaire. L'enregistrement d'application Azure AD n'est requis que pour une intégration production.
|
|
12
|
-
|
|
13
|
-
## Configuration de test
|
|
14
|
-
|
|
15
|
-
### 1. Enregistrer une application Azure AD
|
|
16
|
-
|
|
17
|
-
1. Aller sur [Azure Portal](https://portal.azure.com)
|
|
18
|
-
2. Naviguer vers **Azure Active Directory** → **App registrations**
|
|
19
|
-
3. Cliquer **New registration**
|
|
20
|
-
4. Configurer les permissions Microsoft Graph :
|
|
21
|
-
- `Files.Read.All` (pour lire tous les fichiers)
|
|
22
|
-
- `Sites.Read.All` (pour SharePoint)
|
|
23
|
-
- `User.Read` (pour les informations utilisateur)
|
|
24
|
-
|
|
25
|
-
### 2. Obtenir un token d'accès (Test rapide avec Azure CLI)
|
|
26
|
-
|
|
27
|
-
```bash
|
|
28
|
-
# Installer Azure CLI si nécessaire
|
|
29
|
-
brew install azure-cli
|
|
30
|
-
|
|
31
|
-
# Se connecter à votre compte Microsoft/Office 365
|
|
32
|
-
az login
|
|
33
|
-
|
|
34
|
-
# Obtenir un token pour Microsoft Graph
|
|
35
|
-
az account get-access-token --resource https://graph.microsoft.com/
|
|
36
|
-
```
|
|
37
|
-
|
|
38
|
-
La commande retournera un JSON avec le token dans le champ `accessToken` :
|
|
39
|
-
```json
|
|
40
|
-
{
|
|
41
|
-
"accessToken": "eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiIs...",
|
|
42
|
-
"expiresOn": "2025-01-10 15:30:00.000000",
|
|
43
|
-
...
|
|
44
|
-
}
|
|
45
|
-
```
|
|
46
|
-
|
|
47
|
-
### 3. Configuration pour test rapide
|
|
48
|
-
|
|
49
|
-
Pour un test avec Azure CLI (pas besoin d'enregistrer d'app) :
|
|
50
|
-
|
|
51
|
-
```json
|
|
52
|
-
{
|
|
53
|
-
"clientId": "dummy",
|
|
54
|
-
"clientSecret": "dummy",
|
|
55
|
-
"tenantId": "dummy",
|
|
56
|
-
"accessToken": "YOUR_TOKEN_FROM_AZ_CLI_COMMAND",
|
|
57
|
-
"rootPath": "/",
|
|
58
|
-
"limit": 10
|
|
59
|
-
}
|
|
60
|
-
```
|
|
61
|
-
|
|
62
|
-
> 💡 Les champs `clientId`, `clientSecret`, et `tenantId` peuvent être des valeurs factices quand vous utilisez un token direct d'Azure CLI.
|
|
63
|
-
|
|
64
|
-
### 4. Tester la connexion
|
|
65
|
-
|
|
66
|
-
```bash
|
|
67
|
-
# Lister les plugins disponibles
|
|
68
|
-
npm run dev -- plugins
|
|
69
|
-
|
|
70
|
-
# Vérifier que OneDrive est bien listé
|
|
71
|
-
```
|
|
72
|
-
|
|
73
|
-
### 5. Scanner les documents (test rapide)
|
|
74
|
-
|
|
75
|
-
Utiliser le script de test :
|
|
76
|
-
|
|
77
|
-
```bash
|
|
78
|
-
# Modifier le fichier test-onedrive.js avec vos vraies valeurs
|
|
79
|
-
node test-onedrive.js
|
|
80
|
-
```
|
|
81
|
-
|
|
82
|
-
## Résultats attendus
|
|
83
|
-
|
|
84
|
-
### Scan réussi
|
|
85
|
-
- ✅ Connection: SUCCESS
|
|
86
|
-
- ✅ Documents trouvés avec métadonnées OneDrive
|
|
87
|
-
- ✅ Limitation respectée (paramètre `limit`)
|
|
88
|
-
- ✅ Types de documents variés (Office, PDF, images, etc.)
|
|
89
|
-
|
|
90
|
-
### Structure des documents
|
|
91
|
-
- **Chemin** : Structure de dossiers OneDrive/SharePoint
|
|
92
|
-
- **Métadonnées** : OneDrive ID, download URL, parent path
|
|
93
|
-
- **Types MIME** : Détection automatique basée sur l'extension
|
|
94
|
-
|
|
95
|
-
## Test d'intégration complète
|
|
96
|
-
|
|
97
|
-
### 1. Configuration d'une connexion
|
|
98
|
-
```bash
|
|
99
|
-
echo '{
|
|
100
|
-
"clientId": "YOUR_CLIENT_ID",
|
|
101
|
-
"clientSecret": "YOUR_CLIENT_SECRET",
|
|
102
|
-
"tenantId": "YOUR_TENANT_ID",
|
|
103
|
-
"accessToken": "YOUR_TOKEN",
|
|
104
|
-
"rootPath": "/Documents",
|
|
105
|
-
"limit": 10
|
|
106
|
-
}' > onedrive-config.json
|
|
107
|
-
|
|
108
|
-
npm run dev -- connect onedrive --config onedrive-config.json --name test-onedrive
|
|
109
|
-
```
|
|
110
|
-
|
|
111
|
-
### 2. Scan des documents
|
|
112
|
-
```bash
|
|
113
|
-
npm run dev -- plugin-scan test-onedrive --output onedrive-mapping.csv
|
|
114
|
-
```
|
|
115
|
-
|
|
116
|
-
### 3. Vérification du mapping
|
|
117
|
-
```bash
|
|
118
|
-
head -10 onedrive-mapping.csv
|
|
119
|
-
```
|
|
120
|
-
|
|
121
|
-
Le fichier doit contenir :
|
|
122
|
-
- Colonnes : File Path, Target Folder, Workspace, Metadata (JSON), Permissions
|
|
123
|
-
- Métadonnées JSON avec OneDrive ID, download URL
|
|
124
|
-
- Chemins organisés selon la structure OneDrive
|
|
125
|
-
|
|
126
|
-
## Dépannage
|
|
127
|
-
|
|
128
|
-
### Token expiré
|
|
129
|
-
```
|
|
130
|
-
Error: Request failed with status code 401
|
|
131
|
-
```
|
|
132
|
-
→ Renouveler le token avec `az account get-access-token --resource https://graph.microsoft.com/`
|
|
133
|
-
|
|
134
|
-
### Permissions insuffisantes
|
|
135
|
-
```
|
|
136
|
-
Error: Insufficient privileges to complete the operation
|
|
137
|
-
```
|
|
138
|
-
→ Vérifier les permissions Microsoft Graph de l'application Azure
|
|
139
|
-
|
|
140
|
-
### Dossier introuvable
|
|
141
|
-
```
|
|
142
|
-
Error: The resource could not be found
|
|
143
|
-
```
|
|
144
|
-
→ Vérifier le `rootPath` ou utiliser `/` pour la racine
|
|
145
|
-
|
|
146
|
-
### Limite de débit
|
|
147
|
-
```
|
|
148
|
-
Error: Too many requests
|
|
149
|
-
```
|
|
150
|
-
→ Le plugin a déjà des délais intégrés, augmenter si nécessaire
|
|
151
|
-
|
|
152
|
-
## Performance
|
|
153
|
-
|
|
154
|
-
Avec limit=10 :
|
|
155
|
-
- Durée : ~2-3 secondes
|
|
156
|
-
- Documents : 10 maximum
|
|
157
|
-
- API calls : 1-2 requêtes
|
|
158
|
-
|
|
159
|
-
Avec limit=100 :
|
|
160
|
-
- Durée : ~10-15 secondes
|
|
161
|
-
- Documents : 100 maximum
|
|
162
|
-
- API calls : Pagination automatique
|
|
163
|
-
|
|
164
|
-
## Paramètres utiles
|
|
165
|
-
|
|
166
|
-
- **limit** : Nombre max de documents (utile pour tests)
|
|
167
|
-
- **rootPath** : Dossier spécifique à scanner (ex: `/Documents`)
|
|
168
|
-
- **accessToken** : Token direct (évite l'OAuth flow complexe)
|
|
169
|
-
- **clientId/clientSecret** : Pour l'authentification OAuth2
|
|
170
|
-
|
|
171
|
-
## Types de documents OneDrive observés
|
|
172
|
-
|
|
173
|
-
- **Office** : .docx, .xlsx, .pptx (exportés automatiquement)
|
|
174
|
-
- **PDF** : Documents PDF
|
|
175
|
-
- **Images** : .jpg, .png, .gif
|
|
176
|
-
- **Archives** : .zip, .rar
|
|
177
|
-
- **Texte** : .txt, .csv, .json
|
|
178
|
-
|
|
179
|
-
## Différences avec SharePoint
|
|
180
|
-
|
|
181
|
-
Le plugin fonctionne pour :
|
|
182
|
-
- **OneDrive personnel** : `/me/drive/`
|
|
183
|
-
- **SharePoint** : Modifier l'URL de base si nécessaire
|
|
184
|
-
- **OneDrive Entreprise** : Même API que personnel
|
|
185
|
-
|
|
186
|
-
## Commandes utiles
|
|
187
|
-
|
|
188
|
-
```bash
|
|
189
|
-
# Test rapide de connexion
|
|
190
|
-
node test-onedrive.js
|
|
191
|
-
|
|
192
|
-
# Scan avec limite
|
|
193
|
-
npm run dev -- plugin-scan test-onedrive --limit 5
|
|
194
|
-
|
|
195
|
-
# Import vers dossier local
|
|
196
|
-
npm run dev -- plugin-import test-onedrive ./downloads --limit 3
|
|
197
|
-
```
|
|
@@ -1,447 +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 OneDriveConfig extends PluginConfig {
|
|
16
|
-
clientId: string;
|
|
17
|
-
clientSecret: string;
|
|
18
|
-
tenantId: string;
|
|
19
|
-
accessToken?: string;
|
|
20
|
-
refreshToken?: string;
|
|
21
|
-
rootPath?: string; // e.g., '/Documents' or '/Shared Documents'
|
|
22
|
-
limit?: number;
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
interface OneDriveItem {
|
|
26
|
-
id: string;
|
|
27
|
-
name: string;
|
|
28
|
-
size?: number;
|
|
29
|
-
file?: {
|
|
30
|
-
mimeType: string;
|
|
31
|
-
};
|
|
32
|
-
folder?: {
|
|
33
|
-
childCount: number;
|
|
34
|
-
};
|
|
35
|
-
lastModifiedDateTime: string;
|
|
36
|
-
parentReference?: {
|
|
37
|
-
path: string;
|
|
38
|
-
};
|
|
39
|
-
'@microsoft.graph.downloadUrl'?: string;
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
export default class OneDrivePlugin implements DocumentSourcePlugin {
|
|
43
|
-
readonly name = 'onedrive';
|
|
44
|
-
readonly version = '1.0.0';
|
|
45
|
-
readonly description = 'Microsoft OneDrive/SharePoint document source';
|
|
46
|
-
readonly supportedOperations = ['import', 'export', 'both'] as const;
|
|
47
|
-
|
|
48
|
-
private config?: OneDriveConfig;
|
|
49
|
-
private apiClient?: AxiosInstance;
|
|
50
|
-
private baseUrl = 'https://graph.microsoft.com/v1.0';
|
|
51
|
-
|
|
52
|
-
async testConnection(config: PluginConfig): Promise<boolean> {
|
|
53
|
-
try {
|
|
54
|
-
const client = this.createApiClient(config as OneDriveConfig);
|
|
55
|
-
// Try simpler endpoint first
|
|
56
|
-
const response = await client.get('/me');
|
|
57
|
-
return response.status === 200;
|
|
58
|
-
} catch (error: any) {
|
|
59
|
-
console.error(`OneDrive connection test failed: ${error.message}`);
|
|
60
|
-
if (error.response?.data) {
|
|
61
|
-
console.error('API Response:', error.response.data);
|
|
62
|
-
}
|
|
63
|
-
return false;
|
|
64
|
-
}
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
async scan(config: PluginConfig, options?: PluginImportOptions): Promise<ScanResult> {
|
|
68
|
-
this.config = config as OneDriveConfig;
|
|
69
|
-
this.apiClient = this.createApiClient(this.config);
|
|
70
|
-
|
|
71
|
-
const sources: DocumentSource[] = [];
|
|
72
|
-
const errors: string[] = [];
|
|
73
|
-
let totalSize = 0;
|
|
74
|
-
|
|
75
|
-
try {
|
|
76
|
-
const limit = (this.config as any).limit || (options as any)?.limit;
|
|
77
|
-
console.log(`🔍 Scanning OneDrive documents${limit ? ` (limit: ${limit})` : ''}...`);
|
|
78
|
-
|
|
79
|
-
const rootPath = this.config.rootPath || '/';
|
|
80
|
-
const items = await this.scanFolder(rootPath, options);
|
|
81
|
-
|
|
82
|
-
let processedCount = 0;
|
|
83
|
-
for (const item of items) {
|
|
84
|
-
if (item.file) { // Only process files, not folders
|
|
85
|
-
const source: DocumentSource = {
|
|
86
|
-
id: item.id,
|
|
87
|
-
name: item.name,
|
|
88
|
-
path: this.getRelativePath(item),
|
|
89
|
-
size: item.size || 0,
|
|
90
|
-
mimeType: item.file.mimeType,
|
|
91
|
-
lastModified: new Date(item.lastModifiedDateTime),
|
|
92
|
-
metadata: {
|
|
93
|
-
oneDriveId: item.id,
|
|
94
|
-
downloadUrl: item['@microsoft.graph.downloadUrl'],
|
|
95
|
-
parentPath: item.parentReference?.path
|
|
96
|
-
}
|
|
97
|
-
};
|
|
98
|
-
|
|
99
|
-
// Apply filters
|
|
100
|
-
if (this.shouldIncludeSource(source, options)) {
|
|
101
|
-
sources.push(source);
|
|
102
|
-
totalSize += source.size;
|
|
103
|
-
processedCount++;
|
|
104
|
-
|
|
105
|
-
// Check limit
|
|
106
|
-
if (limit && processedCount >= limit) {
|
|
107
|
-
console.log(`📏 Reached limit of ${limit} files`);
|
|
108
|
-
break;
|
|
109
|
-
}
|
|
110
|
-
}
|
|
111
|
-
}
|
|
112
|
-
}
|
|
113
|
-
|
|
114
|
-
return {
|
|
115
|
-
sources,
|
|
116
|
-
totalCount: sources.length,
|
|
117
|
-
totalSize,
|
|
118
|
-
errors
|
|
119
|
-
};
|
|
120
|
-
} catch (error: any) {
|
|
121
|
-
return {
|
|
122
|
-
sources: [],
|
|
123
|
-
totalCount: 0,
|
|
124
|
-
totalSize: 0,
|
|
125
|
-
errors: [`OneDrive scan failed: ${error.message}`]
|
|
126
|
-
};
|
|
127
|
-
}
|
|
128
|
-
}
|
|
129
|
-
|
|
130
|
-
private async scanFolder(folderPath: string, options?: PluginImportOptions): Promise<OneDriveItem[]> {
|
|
131
|
-
if (!this.apiClient) throw new Error('API client not initialized');
|
|
132
|
-
|
|
133
|
-
const items: OneDriveItem[] = [];
|
|
134
|
-
let nextLink: string | undefined;
|
|
135
|
-
|
|
136
|
-
do {
|
|
137
|
-
try {
|
|
138
|
-
// Handle root path correctly
|
|
139
|
-
let url: string;
|
|
140
|
-
if (nextLink) {
|
|
141
|
-
url = nextLink;
|
|
142
|
-
} else if (!folderPath || folderPath === '/') {
|
|
143
|
-
url = '/me/drive/root/children';
|
|
144
|
-
} else {
|
|
145
|
-
url = `/me/drive/root:${folderPath}:/children`;
|
|
146
|
-
}
|
|
147
|
-
|
|
148
|
-
const response = await this.apiClient.get(url);
|
|
149
|
-
|
|
150
|
-
const folderItems: OneDriveItem[] = response.data.value;
|
|
151
|
-
items.push(...folderItems);
|
|
152
|
-
|
|
153
|
-
// Recursively scan subfolders
|
|
154
|
-
for (const item of folderItems) {
|
|
155
|
-
if (item.folder && item.folder.childCount > 0) {
|
|
156
|
-
const subPath = `${folderPath}/${item.name}`;
|
|
157
|
-
const subItems = await this.scanFolder(subPath, options);
|
|
158
|
-
items.push(...subItems);
|
|
159
|
-
}
|
|
160
|
-
}
|
|
161
|
-
|
|
162
|
-
nextLink = response.data['@odata.nextLink'];
|
|
163
|
-
} catch (error: any) {
|
|
164
|
-
console.warn(`Warning: Failed to scan folder ${folderPath}: ${error.message}`);
|
|
165
|
-
break;
|
|
166
|
-
}
|
|
167
|
-
} while (nextLink);
|
|
168
|
-
|
|
169
|
-
return items;
|
|
170
|
-
}
|
|
171
|
-
|
|
172
|
-
async import(
|
|
173
|
-
config: PluginConfig,
|
|
174
|
-
sources: DocumentSource[],
|
|
175
|
-
targetDir: string,
|
|
176
|
-
options?: PluginImportOptions
|
|
177
|
-
): Promise<ImportResult[]> {
|
|
178
|
-
this.config = config as OneDriveConfig;
|
|
179
|
-
this.apiClient = this.createApiClient(this.config);
|
|
180
|
-
|
|
181
|
-
const results: ImportResult[] = [];
|
|
182
|
-
const batchSize = options?.batchSize || 5; // OneDrive has rate limits
|
|
183
|
-
|
|
184
|
-
// Process in batches to respect API limits
|
|
185
|
-
for (let i = 0; i < sources.length; i += batchSize) {
|
|
186
|
-
const batch = sources.slice(i, i + batchSize);
|
|
187
|
-
|
|
188
|
-
for (const source of batch) {
|
|
189
|
-
const result = await this.importSingle(source, targetDir);
|
|
190
|
-
results.push(result);
|
|
191
|
-
|
|
192
|
-
// Small delay to respect rate limits
|
|
193
|
-
await this.sleep(100);
|
|
194
|
-
}
|
|
195
|
-
}
|
|
196
|
-
|
|
197
|
-
return results;
|
|
198
|
-
}
|
|
199
|
-
|
|
200
|
-
private async importSingle(source: DocumentSource, targetDir: string): Promise<ImportResult> {
|
|
201
|
-
try {
|
|
202
|
-
if (!this.apiClient) throw new Error('API client not initialized');
|
|
203
|
-
|
|
204
|
-
// Get download URL if not in metadata
|
|
205
|
-
let downloadUrl = source.metadata?.downloadUrl;
|
|
206
|
-
|
|
207
|
-
if (!downloadUrl) {
|
|
208
|
-
const response = await this.apiClient.get(`/me/drive/items/${source.id}`);
|
|
209
|
-
downloadUrl = response.data['@microsoft.graph.downloadUrl'];
|
|
210
|
-
}
|
|
211
|
-
|
|
212
|
-
if (!downloadUrl) {
|
|
213
|
-
throw new Error('No download URL available');
|
|
214
|
-
}
|
|
215
|
-
|
|
216
|
-
// Download file
|
|
217
|
-
const response = await axios.get(downloadUrl, {
|
|
218
|
-
responseType: 'stream'
|
|
219
|
-
});
|
|
220
|
-
|
|
221
|
-
const targetPath = path.join(targetDir, source.path);
|
|
222
|
-
const targetDirectory = path.dirname(targetPath);
|
|
223
|
-
|
|
224
|
-
await fs.ensureDir(targetDirectory);
|
|
225
|
-
|
|
226
|
-
const writer = fs.createWriteStream(targetPath);
|
|
227
|
-
response.data.pipe(writer);
|
|
228
|
-
|
|
229
|
-
return new Promise((resolve) => {
|
|
230
|
-
writer.on('finish', () => {
|
|
231
|
-
resolve({
|
|
232
|
-
success: true,
|
|
233
|
-
source,
|
|
234
|
-
localPath: targetPath,
|
|
235
|
-
bytesTransferred: source.size
|
|
236
|
-
});
|
|
237
|
-
});
|
|
238
|
-
|
|
239
|
-
writer.on('error', (error) => {
|
|
240
|
-
resolve({
|
|
241
|
-
success: false,
|
|
242
|
-
source,
|
|
243
|
-
error: error.message
|
|
244
|
-
});
|
|
245
|
-
});
|
|
246
|
-
});
|
|
247
|
-
} catch (error: any) {
|
|
248
|
-
return {
|
|
249
|
-
success: false,
|
|
250
|
-
source,
|
|
251
|
-
error: error.message
|
|
252
|
-
};
|
|
253
|
-
}
|
|
254
|
-
}
|
|
255
|
-
|
|
256
|
-
async export?(
|
|
257
|
-
config: PluginConfig,
|
|
258
|
-
localSources: DocumentSource[],
|
|
259
|
-
options?: PluginExportOptions
|
|
260
|
-
): Promise<ExportResult[]> {
|
|
261
|
-
this.config = config as OneDriveConfig;
|
|
262
|
-
this.apiClient = this.createApiClient(this.config);
|
|
263
|
-
|
|
264
|
-
const results: ExportResult[] = [];
|
|
265
|
-
|
|
266
|
-
for (const source of localSources) {
|
|
267
|
-
try {
|
|
268
|
-
const targetPath = options?.preserveStructure
|
|
269
|
-
? `${options.targetPath}/${source.path}`
|
|
270
|
-
: `${options?.targetPath || ''}/${source.name}`;
|
|
271
|
-
|
|
272
|
-
// Create folder structure if needed
|
|
273
|
-
const folderPath = path.dirname(targetPath);
|
|
274
|
-
if (folderPath && folderPath !== '.') {
|
|
275
|
-
await this.createFolderStructure(folderPath);
|
|
276
|
-
}
|
|
277
|
-
|
|
278
|
-
// Upload file
|
|
279
|
-
const fileContent = await fs.readFile(source.id);
|
|
280
|
-
const uploadUrl = `/me/drive/root:${targetPath}:/content`;
|
|
281
|
-
|
|
282
|
-
await this.apiClient!.put(uploadUrl, fileContent, {
|
|
283
|
-
headers: {
|
|
284
|
-
'Content-Type': source.mimeType
|
|
285
|
-
}
|
|
286
|
-
});
|
|
287
|
-
|
|
288
|
-
results.push({
|
|
289
|
-
success: true,
|
|
290
|
-
targetPath,
|
|
291
|
-
source,
|
|
292
|
-
bytesTransferred: source.size
|
|
293
|
-
});
|
|
294
|
-
} catch (error: any) {
|
|
295
|
-
results.push({
|
|
296
|
-
success: false,
|
|
297
|
-
targetPath: options?.targetPath || '',
|
|
298
|
-
source,
|
|
299
|
-
error: error.message
|
|
300
|
-
});
|
|
301
|
-
}
|
|
302
|
-
}
|
|
303
|
-
|
|
304
|
-
return results;
|
|
305
|
-
}
|
|
306
|
-
|
|
307
|
-
private async createFolderStructure(folderPath: string): Promise<void> {
|
|
308
|
-
if (!this.apiClient) throw new Error('API client not initialized');
|
|
309
|
-
|
|
310
|
-
const parts = folderPath.split('/').filter(part => part.length > 0);
|
|
311
|
-
let currentPath = '';
|
|
312
|
-
|
|
313
|
-
for (const part of parts) {
|
|
314
|
-
currentPath += `/${part}`;
|
|
315
|
-
|
|
316
|
-
try {
|
|
317
|
-
// Try to get the folder
|
|
318
|
-
await this.apiClient.get(`/me/drive/root:${currentPath}`);
|
|
319
|
-
} catch (error: any) {
|
|
320
|
-
if (error.response?.status === 404) {
|
|
321
|
-
// Folder doesn't exist, create it
|
|
322
|
-
const parentPath = path.dirname(currentPath) || '/';
|
|
323
|
-
await this.apiClient.post(`/me/drive/root:${parentPath}:/children`, {
|
|
324
|
-
name: part,
|
|
325
|
-
folder: {}
|
|
326
|
-
});
|
|
327
|
-
}
|
|
328
|
-
}
|
|
329
|
-
}
|
|
330
|
-
}
|
|
331
|
-
|
|
332
|
-
getConfigSchema(): Record<string, any> {
|
|
333
|
-
return {
|
|
334
|
-
type: 'object',
|
|
335
|
-
properties: {
|
|
336
|
-
clientId: {
|
|
337
|
-
type: 'string',
|
|
338
|
-
description: 'Azure App Client ID (can be dummy when using accessToken)',
|
|
339
|
-
required: false
|
|
340
|
-
},
|
|
341
|
-
clientSecret: {
|
|
342
|
-
type: 'string',
|
|
343
|
-
description: 'Azure App Client Secret (can be dummy when using accessToken)',
|
|
344
|
-
required: false
|
|
345
|
-
},
|
|
346
|
-
tenantId: {
|
|
347
|
-
type: 'string',
|
|
348
|
-
description: 'Azure Tenant ID (can be dummy when using accessToken)',
|
|
349
|
-
required: false
|
|
350
|
-
},
|
|
351
|
-
accessToken: {
|
|
352
|
-
type: 'string',
|
|
353
|
-
description: 'OAuth2 Access Token (required - get with az account get-access-token)',
|
|
354
|
-
required: true
|
|
355
|
-
},
|
|
356
|
-
refreshToken: {
|
|
357
|
-
type: 'string',
|
|
358
|
-
description: 'OAuth2 Refresh Token (will be obtained automatically)',
|
|
359
|
-
required: false
|
|
360
|
-
},
|
|
361
|
-
rootPath: {
|
|
362
|
-
type: 'string',
|
|
363
|
-
description: 'Root path to scan (e.g., /Documents)',
|
|
364
|
-
default: '/'
|
|
365
|
-
},
|
|
366
|
-
limit: {
|
|
367
|
-
type: 'number',
|
|
368
|
-
description: 'Maximum number of documents to scan (useful for testing)',
|
|
369
|
-
required: false
|
|
370
|
-
}
|
|
371
|
-
},
|
|
372
|
-
required: ['accessToken']
|
|
373
|
-
};
|
|
374
|
-
}
|
|
375
|
-
|
|
376
|
-
async initialize(config: PluginConfig): Promise<void> {
|
|
377
|
-
this.config = config as OneDriveConfig;
|
|
378
|
-
|
|
379
|
-
// If no access token, we need to authenticate
|
|
380
|
-
if (!this.config.accessToken) {
|
|
381
|
-
console.log('ℹ️ OneDrive authentication required. Please obtain an access token first.');
|
|
382
|
-
console.log('💡 You can use Azure CLI: az account get-access-token --resource https://graph.microsoft.com/');
|
|
383
|
-
throw new Error('OneDrive authentication required. Please provide an access token.');
|
|
384
|
-
}
|
|
385
|
-
|
|
386
|
-
// Set default values for required fields if not provided
|
|
387
|
-
if (!this.config.clientId) this.config.clientId = 'dummy';
|
|
388
|
-
if (!this.config.clientSecret) this.config.clientSecret = 'dummy';
|
|
389
|
-
if (!this.config.tenantId) this.config.tenantId = 'dummy';
|
|
390
|
-
|
|
391
|
-
this.apiClient = this.createApiClient(this.config);
|
|
392
|
-
}
|
|
393
|
-
|
|
394
|
-
async destroy(): Promise<void> {
|
|
395
|
-
this.config = undefined;
|
|
396
|
-
this.apiClient = undefined;
|
|
397
|
-
}
|
|
398
|
-
|
|
399
|
-
private createApiClient(config: OneDriveConfig): AxiosInstance {
|
|
400
|
-
return axios.create({
|
|
401
|
-
baseURL: this.baseUrl,
|
|
402
|
-
headers: {
|
|
403
|
-
'Authorization': `Bearer ${config.accessToken}`,
|
|
404
|
-
'Content-Type': 'application/json'
|
|
405
|
-
},
|
|
406
|
-
timeout: 30000
|
|
407
|
-
});
|
|
408
|
-
}
|
|
409
|
-
|
|
410
|
-
private getRelativePath(item: OneDriveItem): string {
|
|
411
|
-
if (!item.parentReference?.path) {
|
|
412
|
-
return item.name;
|
|
413
|
-
}
|
|
414
|
-
|
|
415
|
-
// Remove the root drive path
|
|
416
|
-
const parentPath = item.parentReference.path
|
|
417
|
-
.replace(/^\/drive\/root:?/, '')
|
|
418
|
-
.replace(/^\//, '');
|
|
419
|
-
|
|
420
|
-
return parentPath ? `${parentPath}/${item.name}` : item.name;
|
|
421
|
-
}
|
|
422
|
-
|
|
423
|
-
private shouldIncludeSource(source: DocumentSource, options?: PluginImportOptions): boolean {
|
|
424
|
-
// Apply size filter
|
|
425
|
-
if (options?.filters?.maxSize && source.size > options.filters.maxSize) {
|
|
426
|
-
return false;
|
|
427
|
-
}
|
|
428
|
-
|
|
429
|
-
// Apply date range filter
|
|
430
|
-
if (options?.filters?.dateRange) {
|
|
431
|
-
const { from, to } = options.filters.dateRange;
|
|
432
|
-
if (from && source.lastModified < from) return false;
|
|
433
|
-
if (to && source.lastModified > to) return false;
|
|
434
|
-
}
|
|
435
|
-
|
|
436
|
-
// Apply MIME type filter
|
|
437
|
-
if (options?.filters?.mimeTypes && !options.filters.mimeTypes.includes(source.mimeType)) {
|
|
438
|
-
return false;
|
|
439
|
-
}
|
|
440
|
-
|
|
441
|
-
return true;
|
|
442
|
-
}
|
|
443
|
-
|
|
444
|
-
private sleep(ms: number): Promise<void> {
|
|
445
|
-
return new Promise(resolve => setTimeout(resolve, ms));
|
|
446
|
-
}
|
|
447
|
-
}
|
|
@@ -1,12 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"name": "onedrive",
|
|
3
|
-
"version": "1.0.0",
|
|
4
|
-
"description": "Microsoft OneDrive document source plugin",
|
|
5
|
-
"author": "HubDoc Tools",
|
|
6
|
-
"main": "index.js",
|
|
7
|
-
"hubdocToolVersion": "^1.0.0",
|
|
8
|
-
"dependencies": {
|
|
9
|
-
"axios": "^1.5.0",
|
|
10
|
-
"fs-extra": "^11.1.0"
|
|
11
|
-
}
|
|
12
|
-
}
|