@dynamicweb/cli 1.0.15 → 1.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +244 -171
- package/bin/commands/command.js +88 -88
- package/bin/commands/config.js +54 -54
- package/bin/commands/database.js +65 -71
- package/bin/commands/env.js +174 -155
- package/bin/commands/files.js +378 -300
- package/bin/commands/install.js +64 -63
- package/bin/commands/login.js +228 -218
- package/bin/commands/query.js +99 -101
- package/bin/commands/swift.js +78 -78
- package/bin/downloader.js +66 -66
- package/bin/extractor.js +28 -28
- package/bin/index.js +73 -61
- package/bin/utils.js +112 -125
- package/package.json +51 -42
package/bin/commands/files.js
CHANGED
|
@@ -1,300 +1,378 @@
|
|
|
1
|
-
import fetch from 'node-fetch';
|
|
2
|
-
import path from 'path';
|
|
3
|
-
import fs from 'fs';
|
|
4
|
-
import FormData from 'form-data';
|
|
5
|
-
import { setupEnv, getAgent } from './env.js';
|
|
6
|
-
import { setupUser } from './login.js';
|
|
7
|
-
import { interactiveConfirm, formatBytes, createThrottledStatusUpdater } from '../utils.js';
|
|
8
|
-
import { downloadWithProgress, tryGetFileNameFromResponse } from '../downloader.js';
|
|
9
|
-
import { extractWithProgress } from '../extractor.js';
|
|
10
|
-
|
|
11
|
-
export function filesCommand() {
|
|
12
|
-
return {
|
|
13
|
-
command: 'files [dirPath] [outPath]',
|
|
14
|
-
describe: 'Handles files',
|
|
15
|
-
builder: (yargs) => {
|
|
16
|
-
return yargs
|
|
17
|
-
.positional('dirPath', {
|
|
18
|
-
describe: 'The directory to list or export'
|
|
19
|
-
})
|
|
20
|
-
.positional('outPath', {
|
|
21
|
-
describe: 'The directory to export the specified directory to',
|
|
22
|
-
default: '.'
|
|
23
|
-
})
|
|
24
|
-
.option('list', {
|
|
25
|
-
alias: 'l',
|
|
26
|
-
type: 'boolean',
|
|
27
|
-
describe: 'Lists all directories and files'
|
|
28
|
-
})
|
|
29
|
-
.option('export', {
|
|
30
|
-
alias: 'e',
|
|
31
|
-
type: 'boolean',
|
|
32
|
-
describe: 'Exports the specified directory and all subdirectories at [dirPath] to [outPath]'
|
|
33
|
-
})
|
|
34
|
-
.option('import', {
|
|
35
|
-
alias: 'i',
|
|
36
|
-
type: 'boolean',
|
|
37
|
-
describe: 'Imports the file at [dirPath] to [outPath]'
|
|
38
|
-
})
|
|
39
|
-
.option('overwrite', {
|
|
40
|
-
alias: 'o',
|
|
41
|
-
type: 'boolean',
|
|
42
|
-
describe: 'Used with import, will overwrite existing files at destination if set to true'
|
|
43
|
-
})
|
|
44
|
-
.option('createEmpty', {
|
|
45
|
-
type: 'boolean',
|
|
46
|
-
describe: 'Used with import, will create a file even if its empty'
|
|
47
|
-
})
|
|
48
|
-
.option('includeFiles', {
|
|
49
|
-
alias: 'f',
|
|
50
|
-
type: 'boolean',
|
|
51
|
-
describe: 'Used with export, includes files in list of directories and files'
|
|
52
|
-
})
|
|
53
|
-
.option('recursive', {
|
|
54
|
-
alias: 'r',
|
|
55
|
-
type: 'boolean',
|
|
56
|
-
describe: 'Used with list, import and export, handles all directories recursively'
|
|
57
|
-
})
|
|
58
|
-
.option('raw', {
|
|
59
|
-
type: 'boolean',
|
|
60
|
-
describe: 'Used with export, keeps zip file instead of unpacking it'
|
|
61
|
-
})
|
|
62
|
-
.option('iamstupid', {
|
|
63
|
-
type: 'boolean',
|
|
64
|
-
describe: 'Includes export of log and cache folders, NOT RECOMMENDED'
|
|
65
|
-
})
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
if (
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
await
|
|
110
|
-
}
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
}
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
}
|
|
200
|
-
|
|
201
|
-
const
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
}
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
}
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
}
|
|
250
|
-
}
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
});
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
}
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
{
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
}
|
|
300
|
-
|
|
1
|
+
import fetch from 'node-fetch';
|
|
2
|
+
import path from 'path';
|
|
3
|
+
import fs from 'fs';
|
|
4
|
+
import FormData from 'form-data';
|
|
5
|
+
import { setupEnv, getAgent } from './env.js';
|
|
6
|
+
import { setupUser } from './login.js';
|
|
7
|
+
import { interactiveConfirm, formatBytes, createThrottledStatusUpdater } from '../utils.js';
|
|
8
|
+
import { downloadWithProgress, tryGetFileNameFromResponse } from '../downloader.js';
|
|
9
|
+
import { extractWithProgress } from '../extractor.js';
|
|
10
|
+
|
|
11
|
+
export function filesCommand() {
|
|
12
|
+
return {
|
|
13
|
+
command: 'files [dirPath] [outPath]',
|
|
14
|
+
describe: 'Handles files',
|
|
15
|
+
builder: (yargs) => {
|
|
16
|
+
return yargs
|
|
17
|
+
.positional('dirPath', {
|
|
18
|
+
describe: 'The directory to list or export'
|
|
19
|
+
})
|
|
20
|
+
.positional('outPath', {
|
|
21
|
+
describe: 'The directory to export the specified directory to',
|
|
22
|
+
default: '.'
|
|
23
|
+
})
|
|
24
|
+
.option('list', {
|
|
25
|
+
alias: 'l',
|
|
26
|
+
type: 'boolean',
|
|
27
|
+
describe: 'Lists all directories and files'
|
|
28
|
+
})
|
|
29
|
+
.option('export', {
|
|
30
|
+
alias: 'e',
|
|
31
|
+
type: 'boolean',
|
|
32
|
+
describe: 'Exports the specified directory and all subdirectories at [dirPath] to [outPath]'
|
|
33
|
+
})
|
|
34
|
+
.option('import', {
|
|
35
|
+
alias: 'i',
|
|
36
|
+
type: 'boolean',
|
|
37
|
+
describe: 'Imports the file at [dirPath] to [outPath]'
|
|
38
|
+
})
|
|
39
|
+
.option('overwrite', {
|
|
40
|
+
alias: 'o',
|
|
41
|
+
type: 'boolean',
|
|
42
|
+
describe: 'Used with import, will overwrite existing files at destination if set to true'
|
|
43
|
+
})
|
|
44
|
+
.option('createEmpty', {
|
|
45
|
+
type: 'boolean',
|
|
46
|
+
describe: 'Used with import, will create a file even if its empty'
|
|
47
|
+
})
|
|
48
|
+
.option('includeFiles', {
|
|
49
|
+
alias: 'f',
|
|
50
|
+
type: 'boolean',
|
|
51
|
+
describe: 'Used with export, includes files in list of directories and files'
|
|
52
|
+
})
|
|
53
|
+
.option('recursive', {
|
|
54
|
+
alias: 'r',
|
|
55
|
+
type: 'boolean',
|
|
56
|
+
describe: 'Used with list, import and export, handles all directories recursively'
|
|
57
|
+
})
|
|
58
|
+
.option('raw', {
|
|
59
|
+
type: 'boolean',
|
|
60
|
+
describe: 'Used with export, keeps zip file instead of unpacking it'
|
|
61
|
+
})
|
|
62
|
+
.option('iamstupid', {
|
|
63
|
+
type: 'boolean',
|
|
64
|
+
describe: 'Includes export of log and cache folders, NOT RECOMMENDED'
|
|
65
|
+
})
|
|
66
|
+
.option('asFile', {
|
|
67
|
+
type: 'boolean',
|
|
68
|
+
alias: 'af',
|
|
69
|
+
describe: 'Forces the command to treat the path as a single file, even if it has no extension.',
|
|
70
|
+
conflicts: 'asDirectory'
|
|
71
|
+
})
|
|
72
|
+
.option('asDirectory', {
|
|
73
|
+
type: 'boolean',
|
|
74
|
+
alias: 'ad',
|
|
75
|
+
describe: 'Forces the command to treat the path as a directory, even if its name contains a dot.',
|
|
76
|
+
conflicts: 'asFile'
|
|
77
|
+
})
|
|
78
|
+
},
|
|
79
|
+
handler: async (argv) => {
|
|
80
|
+
if (argv.verbose) console.info(`Listing directory at: ${argv.dirPath}`)
|
|
81
|
+
await handleFiles(argv)
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
async function handleFiles(argv) {
|
|
87
|
+
let env = await setupEnv(argv);
|
|
88
|
+
let user = await setupUser(argv, env);
|
|
89
|
+
|
|
90
|
+
if (argv.list) {
|
|
91
|
+
let files = (await getFilesStructure(env, user, argv.dirPath, argv.recursive, argv.includeFiles)).model;
|
|
92
|
+
console.log(files.name)
|
|
93
|
+
let hasFiles = files.files?.data && files.files?.data.length !== 0;
|
|
94
|
+
resolveTree(files.directories, '', hasFiles);
|
|
95
|
+
resolveTree(files.files?.data ?? [], '', false);
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
if (argv.export) {
|
|
99
|
+
if (argv.dirPath) {
|
|
100
|
+
|
|
101
|
+
const isFile = argv.asFile || argv.asDirectory
|
|
102
|
+
? argv.asFile
|
|
103
|
+
: path.extname(argv.dirPath) !== '';
|
|
104
|
+
|
|
105
|
+
if (isFile) {
|
|
106
|
+
let parentDirectory = path.dirname(argv.dirPath);
|
|
107
|
+
parentDirectory = parentDirectory === '.' ? '/' : parentDirectory;
|
|
108
|
+
|
|
109
|
+
await download(env, user, parentDirectory, argv.outPath, false, null, true, argv.iamstupid, [argv.dirPath], true);
|
|
110
|
+
} else {
|
|
111
|
+
await download(env, user, argv.dirPath, argv.outPath, true, null, argv.raw, argv.iamstupid, [], false);
|
|
112
|
+
}
|
|
113
|
+
} else {
|
|
114
|
+
await interactiveConfirm('Are you sure you want a full export of files?', async () => {
|
|
115
|
+
console.log('Full export is starting')
|
|
116
|
+
let filesStructure = (await getFilesStructure(env, user, '/', false, true)).model;
|
|
117
|
+
let dirs = filesStructure.directories;
|
|
118
|
+
for (let id = 0; id < dirs.length; id++) {
|
|
119
|
+
const dir = dirs[id];
|
|
120
|
+
await download(env, user, dir.name, argv.outPath, true, null, argv.raw, argv.iamstupid, [], false);
|
|
121
|
+
}
|
|
122
|
+
await download(env, user, '/.', argv.outPath, false, 'Base.zip', argv.raw, argv.iamstupid, Array.from(filesStructure.files.data, f => f.name), false);
|
|
123
|
+
if (argv.raw) console.log('The files in the base "files" folder is in Base.zip, each directory in "files" is in its own zip')
|
|
124
|
+
})
|
|
125
|
+
}
|
|
126
|
+
} else if (argv.import) {
|
|
127
|
+
if (argv.dirPath && argv.outPath) {
|
|
128
|
+
let resolvedPath = path.resolve(argv.dirPath);
|
|
129
|
+
if (argv.recursive) {
|
|
130
|
+
await processDirectory(env, user, resolvedPath, argv.outPath, resolvedPath, argv.createEmpty, true, argv.overwrite);
|
|
131
|
+
} else {
|
|
132
|
+
let filesInDir = getFilesInDirectory(resolvedPath);
|
|
133
|
+
await uploadFiles(env, user, filesInDir, argv.outPath, argv.createEmpty, argv.overwrite);
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
function getFilesInDirectory(dirPath) {
|
|
140
|
+
return fs.statSync(dirPath).isFile() ? [ dirPath ] : fs.readdirSync(dirPath)
|
|
141
|
+
.map(file => path.join(dirPath, file))
|
|
142
|
+
.filter(file => fs.statSync(file).isFile());
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
async function processDirectory(env, user, dirPath, outPath, originalDir, createEmpty, isRoot = false, overwrite = false) {
|
|
146
|
+
let filesInDir = getFilesInDirectory(dirPath);
|
|
147
|
+
if (filesInDir.length > 0)
|
|
148
|
+
await uploadFiles(env, user, filesInDir, isRoot ? outPath : path.join(outPath, path.basename(dirPath)), createEmpty, overwrite);
|
|
149
|
+
|
|
150
|
+
const subDirectories = fs.readdirSync(dirPath)
|
|
151
|
+
.map(subDir => path.join(dirPath, subDir))
|
|
152
|
+
.filter(subDir => fs.statSync(subDir).isDirectory());
|
|
153
|
+
for (let subDir of subDirectories) {
|
|
154
|
+
await processDirectory(env, user, subDir, isRoot ? outPath : path.join(outPath, path.basename(dirPath)), originalDir, createEmpty, false, overwrite);
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
function resolveTree(dirs, indentLevel, parentHasFiles) {
|
|
159
|
+
let end = `└──`
|
|
160
|
+
let mid = `├──`
|
|
161
|
+
for (let id = 0; id < dirs.length; id++) {
|
|
162
|
+
const dir = dirs[id];
|
|
163
|
+
let indentPipe = true;
|
|
164
|
+
if (dirs.length == 1) {
|
|
165
|
+
if (parentHasFiles) {
|
|
166
|
+
console.log(indentLevel + mid, dir.name)
|
|
167
|
+
} else {
|
|
168
|
+
console.log(indentLevel + end, dir.name)
|
|
169
|
+
indentPipe = false;
|
|
170
|
+
}
|
|
171
|
+
} else if (id != dirs.length - 1) {
|
|
172
|
+
console.log(indentLevel + mid, dir.name)
|
|
173
|
+
} else {
|
|
174
|
+
if (parentHasFiles) {
|
|
175
|
+
console.log(indentLevel + mid, dir.name)
|
|
176
|
+
} else {
|
|
177
|
+
console.log(indentLevel + end, dir.name)
|
|
178
|
+
indentPipe = false;
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
let hasFiles = dir.files?.data && dir.files?.data.length !== 0;
|
|
182
|
+
if (indentPipe) {
|
|
183
|
+
resolveTree(dir.directories ?? [], indentLevel + '│\t', hasFiles);
|
|
184
|
+
resolveTree(dir.files?.data ?? [], indentLevel + '│\t', false);
|
|
185
|
+
} else {
|
|
186
|
+
resolveTree(dir.directories ?? [], indentLevel + '\t', hasFiles);
|
|
187
|
+
resolveTree(dir.files?.data ?? [], indentLevel + '\t', false);
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
async function download(env, user, dirPath, outPath, recursive, outname, raw, iamstupid, fileNames, singleFileMode) {
|
|
193
|
+
let excludeDirectories = '';
|
|
194
|
+
if (!iamstupid) {
|
|
195
|
+
excludeDirectories = 'system/log';
|
|
196
|
+
if (dirPath === 'cache.net') {
|
|
197
|
+
return;
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
const { endpoint, data } = prepareDownloadCommandData(dirPath, excludeDirectories, fileNames, recursive, singleFileMode);
|
|
202
|
+
|
|
203
|
+
displayDownloadMessage(dirPath, fileNames, recursive, singleFileMode);
|
|
204
|
+
|
|
205
|
+
const res = await fetch(`${env.protocol}://${env.host}/Admin/Api/${endpoint}`, {
|
|
206
|
+
method: 'POST',
|
|
207
|
+
body: JSON.stringify(data),
|
|
208
|
+
headers: {
|
|
209
|
+
'Authorization': `Bearer ${user.apiKey}`,
|
|
210
|
+
'Content-Type': 'application/json'
|
|
211
|
+
},
|
|
212
|
+
agent: getAgent(env.protocol)
|
|
213
|
+
});
|
|
214
|
+
|
|
215
|
+
const filename = outname || tryGetFileNameFromResponse(res, dirPath);
|
|
216
|
+
if (!filename) return;
|
|
217
|
+
|
|
218
|
+
const filePath = path.resolve(`${path.resolve(outPath)}/${filename}`)
|
|
219
|
+
const updater = createThrottledStatusUpdater();
|
|
220
|
+
|
|
221
|
+
await downloadWithProgress(res, filePath, {
|
|
222
|
+
onData: (received) => {
|
|
223
|
+
updater.update(`Received:\t${formatBytes(received)}`);
|
|
224
|
+
}
|
|
225
|
+
});
|
|
226
|
+
|
|
227
|
+
updater.stop();
|
|
228
|
+
|
|
229
|
+
if (singleFileMode) {
|
|
230
|
+
console.log(`Successfully downloaded: ${filename}`);
|
|
231
|
+
} else {
|
|
232
|
+
console.log(`Finished downloading`, dirPath === '/.' ? '.' : dirPath, 'Recursive=' + recursive);
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
await extractArchive(filename, filePath, outPath, raw);
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
function prepareDownloadCommandData(directoryPath, excludeDirectories, fileNames, recursive, singleFileMode) {
|
|
239
|
+
const data = {
|
|
240
|
+
'DirectoryPath': directoryPath ?? '/',
|
|
241
|
+
'ExcludeDirectories': [excludeDirectories],
|
|
242
|
+
};
|
|
243
|
+
|
|
244
|
+
if (recursive && !singleFileMode) {
|
|
245
|
+
return { endpoint: 'DirectoryDownload', data };
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
data['Ids'] = fileNames;
|
|
249
|
+
return { endpoint: 'FileDownload', data };
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
function displayDownloadMessage(directoryPath, fileNames, recursive, singleFileMode) {
|
|
253
|
+
if (singleFileMode) {
|
|
254
|
+
const fileName = path.basename(fileNames[0] || 'unknown');
|
|
255
|
+
console.log('Downloading file: ' + fileName);
|
|
256
|
+
|
|
257
|
+
return;
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
const directoryPathDisplayName = directoryPath === '/.'
|
|
261
|
+
? 'Base'
|
|
262
|
+
: directoryPath;
|
|
263
|
+
|
|
264
|
+
console.log('Downloading', directoryPathDisplayName, 'Recursive=' + recursive);
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
async function extractArchive(filename, filePath, outPath, raw) {
|
|
268
|
+
if (raw) {
|
|
269
|
+
return;
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
console.log(`\nExtracting ${filename} to ${outPath}`);
|
|
273
|
+
let destinationFilename = filename.replace('.zip', '');
|
|
274
|
+
if (destinationFilename === 'Base')
|
|
275
|
+
destinationFilename = '';
|
|
276
|
+
|
|
277
|
+
const destinationPath = `${path.resolve(outPath)}/${destinationFilename}`;
|
|
278
|
+
const updater = createThrottledStatusUpdater();
|
|
279
|
+
|
|
280
|
+
await extractWithProgress(filePath, destinationPath, {
|
|
281
|
+
onEntry: (processedEntries, totalEntries, percent) => {
|
|
282
|
+
updater.update(`Extracted:\t${processedEntries} of ${totalEntries} files (${percent}%)`);
|
|
283
|
+
}
|
|
284
|
+
});
|
|
285
|
+
|
|
286
|
+
updater.stop();
|
|
287
|
+
console.log(`Finished extracting ${filename} to ${outPath}\n`);
|
|
288
|
+
|
|
289
|
+
fs.unlink(filePath, function(err) {});
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
async function getFilesStructure(env, user, dirPath, recursive, includeFiles) {
|
|
293
|
+
let res = await fetch(`${env.protocol}://${env.host}/Admin/Api/DirectoryAll?DirectoryPath=${dirPath ?? '/'}&recursive=${recursive ?? 'false'}&includeFiles=${includeFiles ?? 'false'}`, {
|
|
294
|
+
method: 'GET',
|
|
295
|
+
headers: {
|
|
296
|
+
'Authorization': `Bearer ${user.apiKey}`
|
|
297
|
+
},
|
|
298
|
+
agent: getAgent(env.protocol)
|
|
299
|
+
});
|
|
300
|
+
if (res.ok) {
|
|
301
|
+
return await res.json();
|
|
302
|
+
} else {
|
|
303
|
+
console.log(res);
|
|
304
|
+
console.log(await res.json());
|
|
305
|
+
process.exit(1);
|
|
306
|
+
}
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
export async function uploadFiles(env, user, localFilePaths, destinationPath, createEmpty = false, overwrite = false) {
|
|
310
|
+
console.log('Uploading files')
|
|
311
|
+
|
|
312
|
+
const chunkSize = 300;
|
|
313
|
+
const chunks = [];
|
|
314
|
+
|
|
315
|
+
for (let i = 0; i < localFilePaths.length; i += chunkSize) {
|
|
316
|
+
chunks.push(localFilePaths.slice(i, i + chunkSize));
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
for (let i = 0; i < chunks.length; i++) {
|
|
320
|
+
console.log(`Uploading chunk ${i + 1} of ${chunks.length}`);
|
|
321
|
+
|
|
322
|
+
const chunk = chunks[i];
|
|
323
|
+
await uploadChunk(env, user, chunk, destinationPath, createEmpty, overwrite);
|
|
324
|
+
|
|
325
|
+
console.log(`Finished uploading chunk ${i + 1} of ${chunks.length}`);
|
|
326
|
+
}
|
|
327
|
+
|
|
328
|
+
console.log(`Finished uploading files. Total files: ${localFilePaths.length}, total chunks: ${chunks.length}`);
|
|
329
|
+
}
|
|
330
|
+
|
|
331
|
+
async function uploadChunk(env, user, filePathsChunk, destinationPath, createEmpty, overwrite) {
|
|
332
|
+
const form = new FormData();
|
|
333
|
+
form.append('path', destinationPath);
|
|
334
|
+
form.append('skipExistingFiles', String(!overwrite));
|
|
335
|
+
form.append('allowOverwrite', String(overwrite));
|
|
336
|
+
|
|
337
|
+
filePathsChunk.forEach(fileToUpload => {
|
|
338
|
+
console.log(`${fileToUpload}`)
|
|
339
|
+
form.append('files', fs.createReadStream(path.resolve(fileToUpload)));
|
|
340
|
+
});
|
|
341
|
+
|
|
342
|
+
const res = await fetch(`${env.protocol}://${env.host}/Admin/Api/Upload?` + new URLSearchParams({"createEmptyFiles": createEmpty, "createMissingDirectories": true}), {
|
|
343
|
+
method: 'POST',
|
|
344
|
+
body: form,
|
|
345
|
+
headers: {
|
|
346
|
+
'Authorization': `Bearer ${user.apiKey}`
|
|
347
|
+
},
|
|
348
|
+
agent: getAgent(env.protocol)
|
|
349
|
+
});
|
|
350
|
+
|
|
351
|
+
if (res.ok) {
|
|
352
|
+
console.log(await res.json())
|
|
353
|
+
}
|
|
354
|
+
else {
|
|
355
|
+
console.log(res)
|
|
356
|
+
console.log(await res.json())
|
|
357
|
+
process.exit(1);
|
|
358
|
+
}
|
|
359
|
+
}
|
|
360
|
+
|
|
361
|
+
export function resolveFilePath(filePath) {
|
|
362
|
+
let p = path.parse(path.resolve(filePath))
|
|
363
|
+
let regex = wildcardToRegExp(p.base);
|
|
364
|
+
let resolvedPath = fs.readdirSync(p.dir).filter((allFilesPaths) => allFilesPaths.match(regex) !== null)[0]
|
|
365
|
+
if (resolvedPath === undefined)
|
|
366
|
+
{
|
|
367
|
+
console.log('Could not find any files with the name ' + filePath);
|
|
368
|
+
process.exit(1);
|
|
369
|
+
}
|
|
370
|
+
return path.join(p.dir, resolvedPath);
|
|
371
|
+
}
|
|
372
|
+
|
|
373
|
+
|
|
374
|
+
function wildcardToRegExp(wildcard) {
|
|
375
|
+
const escaped = wildcard.replace(/[.+?^${}()|[\]\\]/g, '\\$&');
|
|
376
|
+
return new RegExp('^' + escaped.replace(/\*/g, '.*') + '$');
|
|
377
|
+
}
|
|
378
|
+
|