appwrite-utils-cli 0.9.997 → 0.9.998
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 +1 -0
- package/dist/main.js +55 -56
- package/dist/migrations/transfer.js +3 -2
- package/dist/utilsController.js +0 -1
- package/package.json +1 -1
- package/src/main.ts +96 -82
- package/src/migrations/transfer.ts +17 -10
- package/src/utilsController.ts +0 -6
package/README.md
CHANGED
@@ -147,6 +147,7 @@ This updated CLI ensures that developers have robust tools at their fingertips t
|
|
147
147
|
|
148
148
|
## Changelog
|
149
149
|
|
150
|
+
- 0.9.998: Fixed transfer finally, added `--targetDbId` and `--sourceDbId` as aliases
|
150
151
|
- 0.9.994: Added function deployment management, in BETA, and fixed document transfer between databases
|
151
152
|
- 0.9.993: Fixed `updateFunctionSpecifications` resetting functions to default with undefined values (oops)
|
152
153
|
- 0.9.992: Added `updateFunctionSpecifications` which lists functions and specifications to allow you to update your functions max CPU and RAM usage per-function
|
package/dist/main.js
CHANGED
@@ -79,12 +79,12 @@ const argv = yargs(hideBin(process.argv))
|
|
79
79
|
description: "Transfer data between databases or collections",
|
80
80
|
})
|
81
81
|
.option("fromDbId", {
|
82
|
-
alias: ["fromDb"],
|
82
|
+
alias: ["fromDb", "sourceDbId", "sourceDb"],
|
83
83
|
type: "string",
|
84
84
|
description: "Set the source database ID for transfer",
|
85
85
|
})
|
86
86
|
.option("toDbId", {
|
87
|
-
alias: ["toDb"],
|
87
|
+
alias: ["toDb", "targetDbId", "targetDb"],
|
88
88
|
type: "string",
|
89
89
|
description: "Set the destination database ID for transfer",
|
90
90
|
})
|
@@ -141,7 +141,7 @@ const argv = yargs(hideBin(process.argv))
|
|
141
141
|
"s-4vcpu-4gb",
|
142
142
|
"s-4vcpu-8gb",
|
143
143
|
"s-8vcpu-4gb",
|
144
|
-
"s-8vcpu-8gb"
|
144
|
+
"s-8vcpu-8gb",
|
145
145
|
],
|
146
146
|
})
|
147
147
|
.parse();
|
@@ -264,66 +264,65 @@ async function main() {
|
|
264
264
|
await controller.importData(options);
|
265
265
|
}
|
266
266
|
if (parsedArgv.transfer) {
|
267
|
-
|
268
|
-
|
269
|
-
|
270
|
-
|
271
|
-
|
272
|
-
|
273
|
-
|
274
|
-
|
275
|
-
|
276
|
-
|
277
|
-
|
278
|
-
|
279
|
-
|
280
|
-
}
|
281
|
-
const remoteClient = getClient(parsedArgv.remoteEndpoint, parsedArgv.remoteProjectId, parsedArgv.remoteApiKey);
|
282
|
-
targetDatabases = new Databases(remoteClient);
|
283
|
-
targetStorage = new Storage(remoteClient);
|
284
|
-
const remoteDbs = await fetchAllDatabases(targetDatabases);
|
285
|
-
toDb = remoteDbs.find((db) => db.$id === parsedArgv.toDbId);
|
286
|
-
}
|
287
|
-
else {
|
288
|
-
toDb = (await controller.getDatabasesByIds([parsedArgv.toDbId]))[0];
|
289
|
-
}
|
290
|
-
if (!fromDb || !toDb) {
|
291
|
-
throw new Error("Source or target database not found");
|
267
|
+
const isRemote = !!parsedArgv.remoteEndpoint;
|
268
|
+
let fromDb, toDb;
|
269
|
+
let targetDatabases;
|
270
|
+
let targetStorage;
|
271
|
+
// Only fetch databases if database IDs are provided
|
272
|
+
if (parsedArgv.fromDbId && parsedArgv.toDbId) {
|
273
|
+
console.log(chalk.blue(`Starting database transfer from ${parsedArgv.fromDbId} to ${parsedArgv.toDbId}`));
|
274
|
+
fromDb = (await controller.getDatabasesByIds([parsedArgv.fromDbId]))[0];
|
275
|
+
if (isRemote) {
|
276
|
+
if (!parsedArgv.remoteEndpoint ||
|
277
|
+
!parsedArgv.remoteProjectId ||
|
278
|
+
!parsedArgv.remoteApiKey) {
|
279
|
+
throw new Error("Remote transfer details are missing");
|
292
280
|
}
|
281
|
+
const remoteClient = getClient(parsedArgv.remoteEndpoint, parsedArgv.remoteProjectId, parsedArgv.remoteApiKey);
|
282
|
+
targetDatabases = new Databases(remoteClient);
|
283
|
+
targetStorage = new Storage(remoteClient);
|
284
|
+
const remoteDbs = await fetchAllDatabases(targetDatabases);
|
285
|
+
toDb = remoteDbs.find((db) => db.$id === parsedArgv.toDbId);
|
293
286
|
}
|
294
|
-
|
295
|
-
|
296
|
-
if (parsedArgv.fromBucketId) {
|
297
|
-
sourceBucket = await controller.storage?.getBucket(parsedArgv.fromBucketId);
|
287
|
+
else {
|
288
|
+
toDb = (await controller.getDatabasesByIds([parsedArgv.toDbId]))[0];
|
298
289
|
}
|
299
|
-
if (
|
300
|
-
|
301
|
-
|
302
|
-
|
303
|
-
|
304
|
-
|
305
|
-
|
306
|
-
|
307
|
-
|
308
|
-
|
290
|
+
if (!fromDb || !toDb) {
|
291
|
+
throw new Error("Source or target database not found");
|
292
|
+
}
|
293
|
+
}
|
294
|
+
// Handle storage setup
|
295
|
+
let sourceBucket, targetBucket;
|
296
|
+
if (parsedArgv.fromBucketId) {
|
297
|
+
sourceBucket = await controller.storage?.getBucket(parsedArgv.fromBucketId);
|
298
|
+
}
|
299
|
+
if (parsedArgv.toBucketId) {
|
300
|
+
if (isRemote) {
|
301
|
+
if (!targetStorage) {
|
302
|
+
const remoteClient = getClient(parsedArgv.remoteEndpoint, parsedArgv.remoteProjectId, parsedArgv.remoteApiKey);
|
303
|
+
targetStorage = new Storage(remoteClient);
|
309
304
|
}
|
305
|
+
targetBucket = await targetStorage?.getBucket(parsedArgv.toBucketId);
|
310
306
|
}
|
311
|
-
|
312
|
-
|
313
|
-
throw new Error("No source database or bucket specified for transfer");
|
307
|
+
else {
|
308
|
+
targetBucket = await controller.storage?.getBucket(parsedArgv.toBucketId);
|
314
309
|
}
|
315
|
-
const transferOptions = {
|
316
|
-
isRemote,
|
317
|
-
fromDb,
|
318
|
-
targetDb: toDb,
|
319
|
-
transferEndpoint: parsedArgv.remoteEndpoint,
|
320
|
-
transferProject: parsedArgv.remoteProjectId,
|
321
|
-
transferKey: parsedArgv.remoteApiKey,
|
322
|
-
sourceBucket: sourceBucket,
|
323
|
-
targetBucket: targetBucket,
|
324
|
-
};
|
325
|
-
await controller.transferData(transferOptions);
|
326
310
|
}
|
311
|
+
// Validate that at least one transfer type is specified
|
312
|
+
if (!fromDb && !sourceBucket) {
|
313
|
+
throw new Error("No source database or bucket specified for transfer");
|
314
|
+
}
|
315
|
+
const transferOptions = {
|
316
|
+
isRemote,
|
317
|
+
fromDb,
|
318
|
+
targetDb: toDb,
|
319
|
+
transferEndpoint: parsedArgv.remoteEndpoint,
|
320
|
+
transferProject: parsedArgv.remoteProjectId,
|
321
|
+
transferKey: parsedArgv.remoteApiKey,
|
322
|
+
sourceBucket: sourceBucket,
|
323
|
+
targetBucket: targetBucket,
|
324
|
+
};
|
325
|
+
await controller.transferData(transferOptions);
|
327
326
|
}
|
328
327
|
}
|
329
328
|
}
|
@@ -249,6 +249,7 @@ export const transferDocumentsBetweenDbsLocalToRemote = async (localDb, endpoint
|
|
249
249
|
* @return {Promise<void>} A promise that resolves when the transfer is complete.
|
250
250
|
*/
|
251
251
|
export const transferDatabaseLocalToLocal = async (localDb, fromDbId, targetDbId) => {
|
252
|
+
console.log(chalk.blue(`Starting database transfer from ${fromDbId} to ${targetDbId}`));
|
252
253
|
// Get all collections from source database
|
253
254
|
const sourceCollections = await fetchAllCollections(fromDbId, localDb);
|
254
255
|
console.log(chalk.blue(`Found ${sourceCollections.length} collections in source database`));
|
@@ -296,12 +297,12 @@ export const transferDatabaseLocalToLocal = async (localDb, fromDbId, targetDbId
|
|
296
297
|
for (const index of collection.indexes) {
|
297
298
|
const existingIndex = existingIndexes.indexes.find((idx) => idx.key === index.key);
|
298
299
|
if (!existingIndex) {
|
299
|
-
await createOrUpdateIndex(targetDbId, localDb, targetCollection.$id, index);
|
300
|
+
await tryAwaitWithRetry(async () => createOrUpdateIndex(targetDbId, localDb, targetCollection.$id, index));
|
300
301
|
console.log(chalk.green(`Index ${index.key} created`));
|
301
302
|
}
|
302
303
|
else {
|
303
304
|
console.log(chalk.blue(`Index ${index.key} exists, checking for updates...`));
|
304
|
-
await createOrUpdateIndex(targetDbId, localDb, targetCollection.$id, index);
|
305
|
+
await tryAwaitWithRetry(async () => createOrUpdateIndex(targetDbId, localDb, targetCollection.$id, index));
|
305
306
|
}
|
306
307
|
}
|
307
308
|
// Transfer documents
|
package/dist/utilsController.js
CHANGED
@@ -314,7 +314,6 @@ export class UtilsController {
|
|
314
314
|
if (!fromDb || !targetDb) {
|
315
315
|
throw new Error("Source or target database not found");
|
316
316
|
}
|
317
|
-
console.log(chalk.blue(`Starting database transfer from ${fromDb.$id} to ${targetDb.$id}`));
|
318
317
|
if (options.isRemote && targetClient) {
|
319
318
|
await transferDatabaseLocalToRemote(sourceClient, options.transferEndpoint, options.transferProject, options.transferKey, fromDb.$id, targetDb.$id);
|
320
319
|
}
|
package/package.json
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
{
|
2
2
|
"name": "appwrite-utils-cli",
|
3
3
|
"description": "Appwrite Utility Functions to help with database management, data conversion, data import, migrations, and much more. Meant to be used as a CLI tool, I do not recommend installing this in frontend environments.",
|
4
|
-
"version": "0.9.
|
4
|
+
"version": "0.9.998",
|
5
5
|
"main": "src/main.ts",
|
6
6
|
"type": "module",
|
7
7
|
"repository": {
|
package/src/main.ts
CHANGED
@@ -120,12 +120,12 @@ const argv = yargs(hideBin(process.argv))
|
|
120
120
|
description: "Transfer data between databases or collections",
|
121
121
|
})
|
122
122
|
.option("fromDbId", {
|
123
|
-
alias: ["fromDb"],
|
123
|
+
alias: ["fromDb", "sourceDbId", "sourceDb"],
|
124
124
|
type: "string",
|
125
125
|
description: "Set the source database ID for transfer",
|
126
126
|
})
|
127
127
|
.option("toDbId", {
|
128
|
-
alias: ["toDb"],
|
128
|
+
alias: ["toDb", "targetDbId", "targetDb"],
|
129
129
|
type: "string",
|
130
130
|
description: "Set the destination database ID for transfer",
|
131
131
|
})
|
@@ -182,7 +182,7 @@ const argv = yargs(hideBin(process.argv))
|
|
182
182
|
"s-4vcpu-4gb",
|
183
183
|
"s-4vcpu-8gb",
|
184
184
|
"s-8vcpu-4gb",
|
185
|
-
"s-8vcpu-8gb"
|
185
|
+
"s-8vcpu-8gb",
|
186
186
|
],
|
187
187
|
})
|
188
188
|
.parse() as ParsedArgv;
|
@@ -220,15 +220,32 @@ async function main() {
|
|
220
220
|
|
221
221
|
if (parsedArgv.updateFunctionSpec) {
|
222
222
|
if (!parsedArgv.functionId || !parsedArgv.specification) {
|
223
|
-
throw new Error(
|
223
|
+
throw new Error(
|
224
|
+
"Function ID and specification are required for updating function specs"
|
225
|
+
);
|
224
226
|
}
|
225
|
-
console.log(
|
226
|
-
|
227
|
-
|
228
|
-
|
227
|
+
console.log(
|
228
|
+
chalk.yellow(
|
229
|
+
`Updating function specification for ${parsedArgv.functionId} to ${parsedArgv.specification}, checking if specification exists...`
|
230
|
+
)
|
231
|
+
);
|
232
|
+
const specifications = await listSpecifications(
|
233
|
+
controller.appwriteServer!
|
234
|
+
);
|
235
|
+
if (
|
236
|
+
!specifications.specifications.some(
|
237
|
+
(s: { slug: string }) => s.slug === parsedArgv.specification
|
238
|
+
)
|
239
|
+
) {
|
240
|
+
console.log(
|
241
|
+
chalk.red(`Specification ${parsedArgv.specification} not found`)
|
242
|
+
);
|
229
243
|
return;
|
230
244
|
}
|
231
|
-
await controller.updateFunctionSpecifications(
|
245
|
+
await controller.updateFunctionSpecifications(
|
246
|
+
parsedArgv.functionId,
|
247
|
+
parsedArgv.specification as Specification
|
248
|
+
);
|
232
249
|
}
|
233
250
|
|
234
251
|
// Add default databases if not specified
|
@@ -332,91 +349,88 @@ async function main() {
|
|
332
349
|
}
|
333
350
|
|
334
351
|
if (parsedArgv.transfer) {
|
335
|
-
|
336
|
-
|
337
|
-
|
338
|
-
|
339
|
-
let targetStorage: Storage | undefined;
|
352
|
+
const isRemote = !!parsedArgv.remoteEndpoint;
|
353
|
+
let fromDb, toDb: Models.Database | undefined;
|
354
|
+
let targetDatabases: Databases | undefined;
|
355
|
+
let targetStorage: Storage | undefined;
|
340
356
|
|
341
|
-
|
342
|
-
|
343
|
-
|
344
|
-
|
345
|
-
|
357
|
+
// Only fetch databases if database IDs are provided
|
358
|
+
if (parsedArgv.fromDbId && parsedArgv.toDbId) {
|
359
|
+
console.log(
|
360
|
+
chalk.blue(
|
361
|
+
`Starting database transfer from ${parsedArgv.fromDbId} to ${parsedArgv.toDbId}`
|
362
|
+
)
|
363
|
+
);
|
364
|
+
fromDb = (await controller.getDatabasesByIds([parsedArgv.fromDbId]))[0];
|
346
365
|
|
347
|
-
|
348
|
-
|
349
|
-
|
350
|
-
|
351
|
-
|
352
|
-
|
353
|
-
|
354
|
-
}
|
355
|
-
const remoteClient = getClient(
|
356
|
-
parsedArgv.remoteEndpoint,
|
357
|
-
parsedArgv.remoteProjectId,
|
358
|
-
parsedArgv.remoteApiKey
|
359
|
-
);
|
360
|
-
targetDatabases = new Databases(remoteClient);
|
361
|
-
targetStorage = new Storage(remoteClient);
|
362
|
-
const remoteDbs = await fetchAllDatabases(targetDatabases);
|
363
|
-
toDb = remoteDbs.find((db) => db.$id === parsedArgv.toDbId);
|
364
|
-
} else {
|
365
|
-
toDb = (await controller.getDatabasesByIds([parsedArgv.toDbId]))[0];
|
366
|
-
}
|
367
|
-
|
368
|
-
if (!fromDb || !toDb) {
|
369
|
-
throw new Error("Source or target database not found");
|
366
|
+
if (isRemote) {
|
367
|
+
if (
|
368
|
+
!parsedArgv.remoteEndpoint ||
|
369
|
+
!parsedArgv.remoteProjectId ||
|
370
|
+
!parsedArgv.remoteApiKey
|
371
|
+
) {
|
372
|
+
throw new Error("Remote transfer details are missing");
|
370
373
|
}
|
374
|
+
const remoteClient = getClient(
|
375
|
+
parsedArgv.remoteEndpoint,
|
376
|
+
parsedArgv.remoteProjectId,
|
377
|
+
parsedArgv.remoteApiKey
|
378
|
+
);
|
379
|
+
targetDatabases = new Databases(remoteClient);
|
380
|
+
targetStorage = new Storage(remoteClient);
|
381
|
+
const remoteDbs = await fetchAllDatabases(targetDatabases);
|
382
|
+
toDb = remoteDbs.find((db) => db.$id === parsedArgv.toDbId);
|
383
|
+
} else {
|
384
|
+
toDb = (await controller.getDatabasesByIds([parsedArgv.toDbId]))[0];
|
371
385
|
}
|
372
386
|
|
373
|
-
|
374
|
-
|
375
|
-
if (parsedArgv.fromBucketId) {
|
376
|
-
sourceBucket = await controller.storage?.getBucket(
|
377
|
-
parsedArgv.fromBucketId
|
378
|
-
);
|
387
|
+
if (!fromDb || !toDb) {
|
388
|
+
throw new Error("Source or target database not found");
|
379
389
|
}
|
380
|
-
|
381
|
-
|
382
|
-
|
383
|
-
|
384
|
-
|
385
|
-
|
386
|
-
|
387
|
-
|
388
|
-
|
389
|
-
|
390
|
-
|
391
|
-
|
392
|
-
|
393
|
-
|
394
|
-
|
395
|
-
parsedArgv.
|
390
|
+
}
|
391
|
+
|
392
|
+
// Handle storage setup
|
393
|
+
let sourceBucket, targetBucket;
|
394
|
+
if (parsedArgv.fromBucketId) {
|
395
|
+
sourceBucket = await controller.storage?.getBucket(
|
396
|
+
parsedArgv.fromBucketId
|
397
|
+
);
|
398
|
+
}
|
399
|
+
if (parsedArgv.toBucketId) {
|
400
|
+
if (isRemote) {
|
401
|
+
if (!targetStorage) {
|
402
|
+
const remoteClient = getClient(
|
403
|
+
parsedArgv.remoteEndpoint!,
|
404
|
+
parsedArgv.remoteProjectId!,
|
405
|
+
parsedArgv.remoteApiKey!
|
396
406
|
);
|
407
|
+
targetStorage = new Storage(remoteClient);
|
397
408
|
}
|
398
|
-
|
399
|
-
|
400
|
-
|
401
|
-
|
402
|
-
throw new Error(
|
403
|
-
"No source database or bucket specified for transfer"
|
409
|
+
targetBucket = await targetStorage?.getBucket(parsedArgv.toBucketId);
|
410
|
+
} else {
|
411
|
+
targetBucket = await controller.storage?.getBucket(
|
412
|
+
parsedArgv.toBucketId
|
404
413
|
);
|
405
414
|
}
|
415
|
+
}
|
406
416
|
|
407
|
-
|
408
|
-
|
409
|
-
|
410
|
-
targetDb: toDb,
|
411
|
-
transferEndpoint: parsedArgv.remoteEndpoint,
|
412
|
-
transferProject: parsedArgv.remoteProjectId,
|
413
|
-
transferKey: parsedArgv.remoteApiKey,
|
414
|
-
sourceBucket: sourceBucket,
|
415
|
-
targetBucket: targetBucket,
|
416
|
-
};
|
417
|
-
|
418
|
-
await controller.transferData(transferOptions);
|
417
|
+
// Validate that at least one transfer type is specified
|
418
|
+
if (!fromDb && !sourceBucket) {
|
419
|
+
throw new Error("No source database or bucket specified for transfer");
|
419
420
|
}
|
421
|
+
|
422
|
+
const transferOptions: TransferOptions = {
|
423
|
+
isRemote,
|
424
|
+
fromDb,
|
425
|
+
targetDb: toDb,
|
426
|
+
transferEndpoint: parsedArgv.remoteEndpoint,
|
427
|
+
transferProject: parsedArgv.remoteProjectId,
|
428
|
+
transferKey: parsedArgv.remoteApiKey,
|
429
|
+
sourceBucket: sourceBucket,
|
430
|
+
targetBucket: targetBucket,
|
431
|
+
};
|
432
|
+
|
433
|
+
await controller.transferData(transferOptions);
|
420
434
|
}
|
421
435
|
}
|
422
436
|
}
|
@@ -439,6 +439,9 @@ export const transferDatabaseLocalToLocal = async (
|
|
439
439
|
fromDbId: string,
|
440
440
|
targetDbId: string
|
441
441
|
) => {
|
442
|
+
console.log(
|
443
|
+
chalk.blue(`Starting database transfer from ${fromDbId} to ${targetDbId}`)
|
444
|
+
);
|
442
445
|
// Get all collections from source database
|
443
446
|
const sourceCollections = await fetchAllCollections(fromDbId, localDb);
|
444
447
|
console.log(
|
@@ -557,22 +560,26 @@ export const transferDatabaseLocalToLocal = async (
|
|
557
560
|
);
|
558
561
|
|
559
562
|
if (!existingIndex) {
|
560
|
-
await
|
561
|
-
|
562
|
-
|
563
|
-
|
564
|
-
|
563
|
+
await tryAwaitWithRetry(async () =>
|
564
|
+
createOrUpdateIndex(
|
565
|
+
targetDbId,
|
566
|
+
localDb,
|
567
|
+
targetCollection.$id,
|
568
|
+
index as any
|
569
|
+
)
|
565
570
|
);
|
566
571
|
console.log(chalk.green(`Index ${index.key} created`));
|
567
572
|
} else {
|
568
573
|
console.log(
|
569
574
|
chalk.blue(`Index ${index.key} exists, checking for updates...`)
|
570
575
|
);
|
571
|
-
await
|
572
|
-
|
573
|
-
|
574
|
-
|
575
|
-
|
576
|
+
await tryAwaitWithRetry(async () =>
|
577
|
+
createOrUpdateIndex(
|
578
|
+
targetDbId,
|
579
|
+
localDb,
|
580
|
+
targetCollection.$id,
|
581
|
+
index as any
|
582
|
+
)
|
576
583
|
);
|
577
584
|
}
|
578
585
|
}
|
package/src/utilsController.ts
CHANGED
@@ -468,12 +468,6 @@ export class UtilsController {
|
|
468
468
|
throw new Error("Source or target database not found");
|
469
469
|
}
|
470
470
|
|
471
|
-
console.log(
|
472
|
-
chalk.blue(
|
473
|
-
`Starting database transfer from ${fromDb.$id} to ${targetDb.$id}`
|
474
|
-
)
|
475
|
-
);
|
476
|
-
|
477
471
|
if (options.isRemote && targetClient) {
|
478
472
|
await transferDatabaseLocalToRemote(
|
479
473
|
sourceClient,
|