appwrite-utils-cli 1.0.7 → 1.0.8
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 +62 -0
- package/dist/interactiveCLI.d.ts +1 -0
- package/dist/interactiveCLI.js +168 -4
- package/dist/migrations/comprehensiveTransfer.d.ts +66 -0
- package/dist/migrations/comprehensiveTransfer.js +366 -0
- package/dist/migrations/yaml/YamlImportConfigLoader.d.ts +4 -4
- package/package.json +1 -1
- package/src/interactiveCLI.ts +179 -4
- package/src/migrations/comprehensiveTransfer.ts +505 -0
package/README.md
CHANGED
@@ -327,6 +327,68 @@ This updated CLI ensures that developers have robust tools at their fingertips t
|
|
327
327
|
|
328
328
|
## Changelog
|
329
329
|
|
330
|
+
### 1.0.8 - Comprehensive Transfer System with Enhanced Rate Limiting
|
331
|
+
|
332
|
+
**🚀 Complete Cross-Instance Transfer Solution**
|
333
|
+
|
334
|
+
#### Comprehensive Transfer System
|
335
|
+
- **New CLI Option**: `🚀 Comprehensive transfer (users → databases → buckets → functions)` in interactive mode
|
336
|
+
- **Orchestrated Transfer Flow**: Proper execution order (users → databases → buckets → functions) for dependency management
|
337
|
+
- **Cross-Instance Support**: Transfer entire Appwrite configurations between different instances/projects
|
338
|
+
- **Selective Transfer**: Choose which components to transfer (users, databases, buckets, functions)
|
339
|
+
- **Dry Run Mode**: Test transfers without making actual changes
|
340
|
+
|
341
|
+
#### Enhanced Rate Limiting Strategy
|
342
|
+
- **Configurable Limits**: 5 to 100 concurrent operations in steps of 5
|
343
|
+
- **Differentiated Rates**: Smart rate limiting based on operation type:
|
344
|
+
- **General Operations**: Full rate (databases, functions)
|
345
|
+
- **User Operations**: Half rate (more sensitive operations)
|
346
|
+
- **File Operations**: Quarter rate (most bandwidth intensive)
|
347
|
+
- **Visual Feedback**: Real-time rate limit display during transfers
|
348
|
+
- **Intelligent Scaling**: Automatic calculation of optimal rates for different operations
|
349
|
+
|
350
|
+
#### File Transfer Enhancements
|
351
|
+
- **File Validation**: Comprehensive integrity checking (empty files, size limits)
|
352
|
+
- **Retry Logic**: Exponential backoff for failed file transfers
|
353
|
+
- **Error Handling**: Graceful handling of corrupt/invalid files
|
354
|
+
- **Progress Tracking**: Real-time progress for large file transfers
|
355
|
+
|
356
|
+
#### Function Transfer Integration
|
357
|
+
- **Automated Function Migration**: Download from source, redeploy to target
|
358
|
+
- **Temporary Management**: Automatic cleanup of downloaded function code
|
359
|
+
- **Existing Code Integration**: Leverages existing deployment infrastructure
|
360
|
+
- **Configuration Preservation**: Maintains function settings and variables
|
361
|
+
|
362
|
+
#### User Experience Improvements
|
363
|
+
- **Password Reset Warnings**: Clear notifications about Appwrite password limitations
|
364
|
+
- **Interactive Configuration**: Step-by-step prompts for source/target setup
|
365
|
+
- **Comprehensive Reporting**: Detailed transfer summaries with statistics
|
366
|
+
- **Smart Confirmations**: Risk-based confirmations for destructive operations
|
367
|
+
|
368
|
+
#### Technical Implementation
|
369
|
+
- **Rate Limiting**: Uses p-limit for concurrent operation control
|
370
|
+
- **Error Resilience**: Robust error handling with detailed user feedback
|
371
|
+
- **Memory Management**: Efficient processing of large datasets
|
372
|
+
- **Progress Tracking**: Real-time progress bars with ETA calculations
|
373
|
+
|
374
|
+
#### Usage Examples
|
375
|
+
```bash
|
376
|
+
# Interactive mode - select comprehensive transfer
|
377
|
+
npx appwrite-utils-cli@latest appwrite-migrate --it
|
378
|
+
|
379
|
+
# Example rate limiting at 20 concurrent:
|
380
|
+
# - General operations: 20 concurrent
|
381
|
+
# - User operations: 10 concurrent
|
382
|
+
# - File operations: 5 concurrent
|
383
|
+
```
|
384
|
+
|
385
|
+
**Benefits**:
|
386
|
+
- Complete Appwrite instance migration capability
|
387
|
+
- Intelligent rate limiting prevents API throttling
|
388
|
+
- Enhanced file transfer reliability
|
389
|
+
- Comprehensive progress tracking and reporting
|
390
|
+
- Maintains data integrity across transfers
|
391
|
+
|
330
392
|
### 1.0.7 - Forgot to remove debug logs
|
331
393
|
|
332
394
|
### 1.0.6 - Cross-Language Constants Generation
|
package/dist/interactiveCLI.d.ts
CHANGED
package/dist/interactiveCLI.js
CHANGED
@@ -6,6 +6,7 @@ import { fetchAllCollections } from "./collections/methods.js";
|
|
6
6
|
import { listBuckets, createBucket } from "./storage/methods.js";
|
7
7
|
import { Databases, Storage, Client, Compression, Query, Functions, } from "node-appwrite";
|
8
8
|
import { getClient } from "./utils/getClientFromConfig.js";
|
9
|
+
import { ComprehensiveTransfer } from "./migrations/comprehensiveTransfer.js";
|
9
10
|
import { AppwriteFunctionSchema, parseAttribute, PermissionToAppwritePermission, RuntimeSchema, permissionSchema, } from "appwrite-utils";
|
10
11
|
import { ulid } from "ulidx";
|
11
12
|
import chalk from "chalk";
|
@@ -33,6 +34,7 @@ var CHOICES;
|
|
33
34
|
CHOICES["SYNC_DB"] = "\u2B06\uFE0F Push local config to Appwrite";
|
34
35
|
CHOICES["SYNCHRONIZE_CONFIGURATIONS"] = "\uD83D\uDD04 Synchronize configurations - Pull from Appwrite and write to local config";
|
35
36
|
CHOICES["TRANSFER_DATA"] = "\uD83D\uDCE6 Transfer data";
|
37
|
+
CHOICES["COMPREHENSIVE_TRANSFER"] = "\uD83D\uDE80 Comprehensive transfer (users \u2192 databases \u2192 buckets \u2192 functions)";
|
36
38
|
CHOICES["BACKUP_DATABASE"] = "\uD83D\uDCBE Backup database";
|
37
39
|
CHOICES["WIPE_DATABASE"] = "\uD83E\uDDF9 Wipe database";
|
38
40
|
CHOICES["WIPE_COLLECTIONS"] = "\uD83E\uDDF9 Wipe collections";
|
@@ -109,6 +111,9 @@ export class InteractiveCLI {
|
|
109
111
|
await this.initControllerIfNeeded();
|
110
112
|
await this.transferData();
|
111
113
|
break;
|
114
|
+
case CHOICES.COMPREHENSIVE_TRANSFER:
|
115
|
+
await this.comprehensiveTransfer();
|
116
|
+
break;
|
112
117
|
case CHOICES.BACKUP_DATABASE:
|
113
118
|
await this.initControllerIfNeeded();
|
114
119
|
await this.backupDatabase();
|
@@ -1396,13 +1401,13 @@ export class InteractiveCLI {
|
|
1396
1401
|
}));
|
1397
1402
|
}
|
1398
1403
|
async reloadConfig() {
|
1399
|
-
|
1404
|
+
MessageFormatter.progress("Reloading configuration files...", { prefix: "Config" });
|
1400
1405
|
try {
|
1401
1406
|
await this.controller.reloadConfig();
|
1402
|
-
|
1407
|
+
MessageFormatter.success("Configuration files reloaded successfully", { prefix: "Config" });
|
1403
1408
|
}
|
1404
1409
|
catch (error) {
|
1405
|
-
|
1410
|
+
MessageFormatter.error("Failed to reload configuration files", error instanceof Error ? error : new Error(String(error)), { prefix: "Config" });
|
1406
1411
|
}
|
1407
1412
|
}
|
1408
1413
|
async updateFunctionSpec() {
|
@@ -1452,7 +1457,6 @@ export class InteractiveCLI {
|
|
1452
1457
|
try {
|
1453
1458
|
// Check for YAML config first
|
1454
1459
|
const yamlConfigPath = findYamlConfig(this.currentDir);
|
1455
|
-
console.log(`DEBUG: YAML config search from ${this.currentDir}, found: ${yamlConfigPath}`);
|
1456
1460
|
if (yamlConfigPath) {
|
1457
1461
|
this.isUsingTypeScriptConfig = false;
|
1458
1462
|
MessageFormatter.info("Using YAML configuration", { prefix: "Config" });
|
@@ -1509,4 +1513,164 @@ export class InteractiveCLI {
|
|
1509
1513
|
MessageFormatter.error("Migration failed", error instanceof Error ? error : new Error(String(error)), { prefix: "Migration" });
|
1510
1514
|
}
|
1511
1515
|
}
|
1516
|
+
async comprehensiveTransfer() {
|
1517
|
+
MessageFormatter.info("Starting comprehensive transfer configuration...", { prefix: "Transfer" });
|
1518
|
+
try {
|
1519
|
+
// Get source configuration
|
1520
|
+
const sourceConfig = await inquirer.prompt([
|
1521
|
+
{
|
1522
|
+
type: "input",
|
1523
|
+
name: "sourceEndpoint",
|
1524
|
+
message: "Enter the source Appwrite endpoint:",
|
1525
|
+
validate: (input) => input.trim() !== "" || "Endpoint cannot be empty",
|
1526
|
+
},
|
1527
|
+
{
|
1528
|
+
type: "input",
|
1529
|
+
name: "sourceProject",
|
1530
|
+
message: "Enter the source project ID:",
|
1531
|
+
validate: (input) => input.trim() !== "" || "Project ID cannot be empty",
|
1532
|
+
},
|
1533
|
+
{
|
1534
|
+
type: "password",
|
1535
|
+
name: "sourceKey",
|
1536
|
+
message: "Enter the source API key:",
|
1537
|
+
validate: (input) => input.trim() !== "" || "API key cannot be empty",
|
1538
|
+
},
|
1539
|
+
]);
|
1540
|
+
// Get target configuration
|
1541
|
+
const targetConfig = await inquirer.prompt([
|
1542
|
+
{
|
1543
|
+
type: "input",
|
1544
|
+
name: "targetEndpoint",
|
1545
|
+
message: "Enter the target Appwrite endpoint:",
|
1546
|
+
validate: (input) => input.trim() !== "" || "Endpoint cannot be empty",
|
1547
|
+
},
|
1548
|
+
{
|
1549
|
+
type: "input",
|
1550
|
+
name: "targetProject",
|
1551
|
+
message: "Enter the target project ID:",
|
1552
|
+
validate: (input) => input.trim() !== "" || "Project ID cannot be empty",
|
1553
|
+
},
|
1554
|
+
{
|
1555
|
+
type: "password",
|
1556
|
+
name: "targetKey",
|
1557
|
+
message: "Enter the target API key:",
|
1558
|
+
validate: (input) => input.trim() !== "" || "API key cannot be empty",
|
1559
|
+
},
|
1560
|
+
]);
|
1561
|
+
// Get transfer options
|
1562
|
+
const transferOptions = await inquirer.prompt([
|
1563
|
+
{
|
1564
|
+
type: "checkbox",
|
1565
|
+
name: "transferTypes",
|
1566
|
+
message: "Select what to transfer:",
|
1567
|
+
choices: [
|
1568
|
+
{ name: "👥 Users", value: "users", checked: true },
|
1569
|
+
{ name: "🗄️ Databases", value: "databases", checked: true },
|
1570
|
+
{ name: "📦 Storage Buckets", value: "buckets", checked: true },
|
1571
|
+
{ name: "⚡ Functions", value: "functions", checked: true },
|
1572
|
+
],
|
1573
|
+
validate: (input) => input.length > 0 || "Select at least one transfer type",
|
1574
|
+
},
|
1575
|
+
{
|
1576
|
+
type: "list",
|
1577
|
+
name: "concurrencyLimit",
|
1578
|
+
message: "Select concurrency limit:",
|
1579
|
+
choices: [
|
1580
|
+
{ name: "5 (Conservative) - Users: 2, Files: 1", value: 5 },
|
1581
|
+
{ name: "10 (Balanced) - Users: 5, Files: 2", value: 10 },
|
1582
|
+
{ name: "15 - Users: 7, Files: 3", value: 15 },
|
1583
|
+
{ name: "20 - Users: 10, Files: 5", value: 20 },
|
1584
|
+
{ name: "25 - Users: 12, Files: 6", value: 25 },
|
1585
|
+
{ name: "30 - Users: 15, Files: 7", value: 30 },
|
1586
|
+
{ name: "35 - Users: 17, Files: 8", value: 35 },
|
1587
|
+
{ name: "40 - Users: 20, Files: 10", value: 40 },
|
1588
|
+
{ name: "45 - Users: 22, Files: 11", value: 45 },
|
1589
|
+
{ name: "50 - Users: 25, Files: 12", value: 50 },
|
1590
|
+
{ name: "55 - Users: 27, Files: 13", value: 55 },
|
1591
|
+
{ name: "60 - Users: 30, Files: 15", value: 60 },
|
1592
|
+
{ name: "65 - Users: 32, Files: 16", value: 65 },
|
1593
|
+
{ name: "70 - Users: 35, Files: 17", value: 70 },
|
1594
|
+
{ name: "75 - Users: 37, Files: 18", value: 75 },
|
1595
|
+
{ name: "80 - Users: 40, Files: 20", value: 80 },
|
1596
|
+
{ name: "85 - Users: 42, Files: 21", value: 85 },
|
1597
|
+
{ name: "90 - Users: 45, Files: 22", value: 90 },
|
1598
|
+
{ name: "95 - Users: 47, Files: 23", value: 95 },
|
1599
|
+
{ name: "100 (Aggressive) - Users: 50, Files: 25", value: 100 },
|
1600
|
+
],
|
1601
|
+
default: 10,
|
1602
|
+
},
|
1603
|
+
{
|
1604
|
+
type: "confirm",
|
1605
|
+
name: "dryRun",
|
1606
|
+
message: "Run in dry-run mode (no actual changes)?",
|
1607
|
+
default: false,
|
1608
|
+
},
|
1609
|
+
]);
|
1610
|
+
// Confirmation
|
1611
|
+
const { confirmed } = await inquirer.prompt([
|
1612
|
+
{
|
1613
|
+
type: "confirm",
|
1614
|
+
name: "confirmed",
|
1615
|
+
message: `Are you sure you want to ${transferOptions.dryRun ? "dry-run" : "perform"} comprehensive transfer from ${sourceConfig.sourceEndpoint} to ${targetConfig.targetEndpoint}?`,
|
1616
|
+
default: false,
|
1617
|
+
},
|
1618
|
+
]);
|
1619
|
+
if (!confirmed) {
|
1620
|
+
MessageFormatter.info("Transfer cancelled by user", { prefix: "Transfer" });
|
1621
|
+
return;
|
1622
|
+
}
|
1623
|
+
// Important password warning
|
1624
|
+
if (transferOptions.transferTypes.includes("users") && !transferOptions.dryRun) {
|
1625
|
+
MessageFormatter.warning("IMPORTANT: User passwords cannot be transferred due to Appwrite security limitations.", { prefix: "Transfer" });
|
1626
|
+
MessageFormatter.warning("Users will need to reset their passwords after transfer.", { prefix: "Transfer" });
|
1627
|
+
const { continueWithUsers } = await inquirer.prompt([
|
1628
|
+
{
|
1629
|
+
type: "confirm",
|
1630
|
+
name: "continueWithUsers",
|
1631
|
+
message: "Continue with user transfer knowing passwords will be reset?",
|
1632
|
+
default: false,
|
1633
|
+
},
|
1634
|
+
]);
|
1635
|
+
if (!continueWithUsers) {
|
1636
|
+
// Remove users from transfer types
|
1637
|
+
transferOptions.transferTypes = transferOptions.transferTypes.filter((type) => type !== "users");
|
1638
|
+
if (transferOptions.transferTypes.length === 0) {
|
1639
|
+
MessageFormatter.info("No transfer types selected, cancelling", { prefix: "Transfer" });
|
1640
|
+
return;
|
1641
|
+
}
|
1642
|
+
}
|
1643
|
+
}
|
1644
|
+
// Execute comprehensive transfer
|
1645
|
+
const comprehensiveTransferOptions = {
|
1646
|
+
sourceEndpoint: sourceConfig.sourceEndpoint,
|
1647
|
+
sourceProject: sourceConfig.sourceProject,
|
1648
|
+
sourceKey: sourceConfig.sourceKey,
|
1649
|
+
targetEndpoint: targetConfig.targetEndpoint,
|
1650
|
+
targetProject: targetConfig.targetProject,
|
1651
|
+
targetKey: targetConfig.targetKey,
|
1652
|
+
transferUsers: transferOptions.transferTypes.includes("users"),
|
1653
|
+
transferDatabases: transferOptions.transferTypes.includes("databases"),
|
1654
|
+
transferBuckets: transferOptions.transferTypes.includes("buckets"),
|
1655
|
+
transferFunctions: transferOptions.transferTypes.includes("functions"),
|
1656
|
+
concurrencyLimit: transferOptions.concurrencyLimit,
|
1657
|
+
dryRun: transferOptions.dryRun,
|
1658
|
+
};
|
1659
|
+
const transfer = new ComprehensiveTransfer(comprehensiveTransferOptions);
|
1660
|
+
const results = await transfer.execute();
|
1661
|
+
// Display results
|
1662
|
+
if (transferOptions.dryRun) {
|
1663
|
+
MessageFormatter.success("Dry run completed successfully!", { prefix: "Transfer" });
|
1664
|
+
}
|
1665
|
+
else {
|
1666
|
+
MessageFormatter.success("Comprehensive transfer completed!", { prefix: "Transfer" });
|
1667
|
+
if (transferOptions.transferTypes.includes("users") && results.users.transferred > 0) {
|
1668
|
+
MessageFormatter.info("Remember to notify users about password reset requirements", { prefix: "Transfer" });
|
1669
|
+
}
|
1670
|
+
}
|
1671
|
+
}
|
1672
|
+
catch (error) {
|
1673
|
+
MessageFormatter.error("Comprehensive transfer failed", error instanceof Error ? error : new Error(String(error)), { prefix: "Transfer" });
|
1674
|
+
}
|
1675
|
+
}
|
1512
1676
|
}
|
@@ -0,0 +1,66 @@
|
|
1
|
+
export interface ComprehensiveTransferOptions {
|
2
|
+
sourceEndpoint: string;
|
3
|
+
sourceProject: string;
|
4
|
+
sourceKey: string;
|
5
|
+
targetEndpoint: string;
|
6
|
+
targetProject: string;
|
7
|
+
targetKey: string;
|
8
|
+
transferUsers?: boolean;
|
9
|
+
transferDatabases?: boolean;
|
10
|
+
transferBuckets?: boolean;
|
11
|
+
transferFunctions?: boolean;
|
12
|
+
concurrencyLimit?: number;
|
13
|
+
dryRun?: boolean;
|
14
|
+
}
|
15
|
+
export interface TransferResults {
|
16
|
+
users: {
|
17
|
+
transferred: number;
|
18
|
+
skipped: number;
|
19
|
+
failed: number;
|
20
|
+
};
|
21
|
+
databases: {
|
22
|
+
transferred: number;
|
23
|
+
skipped: number;
|
24
|
+
failed: number;
|
25
|
+
};
|
26
|
+
buckets: {
|
27
|
+
transferred: number;
|
28
|
+
skipped: number;
|
29
|
+
failed: number;
|
30
|
+
};
|
31
|
+
functions: {
|
32
|
+
transferred: number;
|
33
|
+
skipped: number;
|
34
|
+
failed: number;
|
35
|
+
};
|
36
|
+
totalTime: number;
|
37
|
+
}
|
38
|
+
export declare class ComprehensiveTransfer {
|
39
|
+
private options;
|
40
|
+
private sourceClient;
|
41
|
+
private targetClient;
|
42
|
+
private sourceUsers;
|
43
|
+
private targetUsers;
|
44
|
+
private sourceDatabases;
|
45
|
+
private targetDatabases;
|
46
|
+
private sourceStorage;
|
47
|
+
private targetStorage;
|
48
|
+
private sourceFunctions;
|
49
|
+
private targetFunctions;
|
50
|
+
private limit;
|
51
|
+
private userLimit;
|
52
|
+
private fileLimit;
|
53
|
+
private results;
|
54
|
+
private startTime;
|
55
|
+
private tempDir;
|
56
|
+
constructor(options: ComprehensiveTransferOptions);
|
57
|
+
execute(): Promise<TransferResults>;
|
58
|
+
private transferAllUsers;
|
59
|
+
private transferAllDatabases;
|
60
|
+
private transferAllBuckets;
|
61
|
+
private transferBucketFiles;
|
62
|
+
private validateAndDownloadFile;
|
63
|
+
private transferAllFunctions;
|
64
|
+
private downloadFunction;
|
65
|
+
private printSummary;
|
66
|
+
}
|