appwrite-utils-cli 1.0.7 → 1.0.9

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.
@@ -15,6 +15,7 @@ import {
15
15
  } from "node-appwrite";
16
16
  import { getClient } from "./utils/getClientFromConfig.js";
17
17
  import type { TransferOptions } from "./migrations/transfer.js";
18
+ import { ComprehensiveTransfer, type ComprehensiveTransferOptions } from "./migrations/comprehensiveTransfer.js";
18
19
  import {
19
20
  AppwriteFunctionSchema,
20
21
  parseAttribute,
@@ -61,6 +62,7 @@ enum CHOICES {
61
62
  SYNC_DB = "⬆️ Push local config to Appwrite",
62
63
  SYNCHRONIZE_CONFIGURATIONS = "🔄 Synchronize configurations - Pull from Appwrite and write to local config",
63
64
  TRANSFER_DATA = "📦 Transfer data",
65
+ COMPREHENSIVE_TRANSFER = "🚀 Comprehensive transfer (users → databases → buckets → functions)",
64
66
  BACKUP_DATABASE = "💾 Backup database",
65
67
  WIPE_DATABASE = "🧹 Wipe database",
66
68
  WIPE_COLLECTIONS = "🧹 Wipe collections",
@@ -145,6 +147,9 @@ export class InteractiveCLI {
145
147
  await this.initControllerIfNeeded();
146
148
  await this.transferData();
147
149
  break;
150
+ case CHOICES.COMPREHENSIVE_TRANSFER:
151
+ await this.comprehensiveTransfer();
152
+ break;
148
153
  case CHOICES.BACKUP_DATABASE:
149
154
  await this.initControllerIfNeeded();
150
155
  await this.backupDatabase();
@@ -1903,12 +1908,12 @@ export class InteractiveCLI {
1903
1908
  }
1904
1909
 
1905
1910
  private async reloadConfig(): Promise<void> {
1906
- console.log(chalk.yellow("Reloading configuration files..."));
1911
+ MessageFormatter.progress("Reloading configuration files...", { prefix: "Config" });
1907
1912
  try {
1908
1913
  await this.controller!.reloadConfig();
1909
- console.log(chalk.green("Configuration files reloaded successfully."));
1914
+ MessageFormatter.success("Configuration files reloaded successfully", { prefix: "Config" });
1910
1915
  } catch (error) {
1911
- console.error(chalk.red("Error reloading configuration files:"), error);
1916
+ MessageFormatter.error("Failed to reload configuration files", error instanceof Error ? error : new Error(String(error)), { prefix: "Config" });
1912
1917
  }
1913
1918
  }
1914
1919
 
@@ -1979,7 +1984,6 @@ export class InteractiveCLI {
1979
1984
  try {
1980
1985
  // Check for YAML config first
1981
1986
  const yamlConfigPath = findYamlConfig(this.currentDir);
1982
- console.log(`DEBUG: YAML config search from ${this.currentDir}, found: ${yamlConfigPath}`);
1983
1987
  if (yamlConfigPath) {
1984
1988
  this.isUsingTypeScriptConfig = false;
1985
1989
  MessageFormatter.info("Using YAML configuration", { prefix: "Config" });
@@ -2043,4 +2047,178 @@ export class InteractiveCLI {
2043
2047
  MessageFormatter.error("Migration failed", error instanceof Error ? error : new Error(String(error)), { prefix: "Migration" });
2044
2048
  }
2045
2049
  }
2050
+
2051
+ private async comprehensiveTransfer(): Promise<void> {
2052
+ MessageFormatter.info("Starting comprehensive transfer configuration...", { prefix: "Transfer" });
2053
+
2054
+ try {
2055
+ // Get source configuration
2056
+ const sourceConfig = await inquirer.prompt([
2057
+ {
2058
+ type: "input",
2059
+ name: "sourceEndpoint",
2060
+ message: "Enter the source Appwrite endpoint:",
2061
+ validate: (input) => input.trim() !== "" || "Endpoint cannot be empty",
2062
+ },
2063
+ {
2064
+ type: "input",
2065
+ name: "sourceProject",
2066
+ message: "Enter the source project ID:",
2067
+ validate: (input) => input.trim() !== "" || "Project ID cannot be empty",
2068
+ },
2069
+ {
2070
+ type: "password",
2071
+ name: "sourceKey",
2072
+ message: "Enter the source API key:",
2073
+ validate: (input) => input.trim() !== "" || "API key cannot be empty",
2074
+ },
2075
+ ]);
2076
+
2077
+ // Get target configuration
2078
+ const targetConfig = await inquirer.prompt([
2079
+ {
2080
+ type: "input",
2081
+ name: "targetEndpoint",
2082
+ message: "Enter the target Appwrite endpoint:",
2083
+ validate: (input) => input.trim() !== "" || "Endpoint cannot be empty",
2084
+ },
2085
+ {
2086
+ type: "input",
2087
+ name: "targetProject",
2088
+ message: "Enter the target project ID:",
2089
+ validate: (input) => input.trim() !== "" || "Project ID cannot be empty",
2090
+ },
2091
+ {
2092
+ type: "password",
2093
+ name: "targetKey",
2094
+ message: "Enter the target API key:",
2095
+ validate: (input) => input.trim() !== "" || "API key cannot be empty",
2096
+ },
2097
+ ]);
2098
+
2099
+ // Get transfer options
2100
+ const transferOptions = await inquirer.prompt([
2101
+ {
2102
+ type: "checkbox",
2103
+ name: "transferTypes",
2104
+ message: "Select what to transfer:",
2105
+ choices: [
2106
+ { name: "👥 Users", value: "users", checked: true },
2107
+ { name: "🗄️ Databases", value: "databases", checked: true },
2108
+ { name: "📦 Storage Buckets", value: "buckets", checked: true },
2109
+ { name: "⚡ Functions", value: "functions", checked: true },
2110
+ ],
2111
+ validate: (input) => input.length > 0 || "Select at least one transfer type",
2112
+ },
2113
+ {
2114
+ type: "list",
2115
+ name: "concurrencyLimit",
2116
+ message: "Select concurrency limit:",
2117
+ choices: [
2118
+ { name: "5 (Conservative) - Users: 2, Files: 1", value: 5 },
2119
+ { name: "10 (Balanced) - Users: 5, Files: 2", value: 10 },
2120
+ { name: "15 - Users: 7, Files: 3", value: 15 },
2121
+ { name: "20 - Users: 10, Files: 5", value: 20 },
2122
+ { name: "25 - Users: 12, Files: 6", value: 25 },
2123
+ { name: "30 - Users: 15, Files: 7", value: 30 },
2124
+ { name: "35 - Users: 17, Files: 8", value: 35 },
2125
+ { name: "40 - Users: 20, Files: 10", value: 40 },
2126
+ { name: "45 - Users: 22, Files: 11", value: 45 },
2127
+ { name: "50 - Users: 25, Files: 12", value: 50 },
2128
+ { name: "55 - Users: 27, Files: 13", value: 55 },
2129
+ { name: "60 - Users: 30, Files: 15", value: 60 },
2130
+ { name: "65 - Users: 32, Files: 16", value: 65 },
2131
+ { name: "70 - Users: 35, Files: 17", value: 70 },
2132
+ { name: "75 - Users: 37, Files: 18", value: 75 },
2133
+ { name: "80 - Users: 40, Files: 20", value: 80 },
2134
+ { name: "85 - Users: 42, Files: 21", value: 85 },
2135
+ { name: "90 - Users: 45, Files: 22", value: 90 },
2136
+ { name: "95 - Users: 47, Files: 23", value: 95 },
2137
+ { name: "100 (Aggressive) - Users: 50, Files: 25", value: 100 },
2138
+ ],
2139
+ default: 10,
2140
+ },
2141
+ {
2142
+ type: "confirm",
2143
+ name: "dryRun",
2144
+ message: "Run in dry-run mode (no actual changes)?",
2145
+ default: false,
2146
+ },
2147
+ ]);
2148
+
2149
+ // Confirmation
2150
+ const { confirmed } = await inquirer.prompt([
2151
+ {
2152
+ type: "confirm",
2153
+ name: "confirmed",
2154
+ message: `Are you sure you want to ${transferOptions.dryRun ? "dry-run" : "perform"} comprehensive transfer from ${sourceConfig.sourceEndpoint} to ${targetConfig.targetEndpoint}?`,
2155
+ default: false,
2156
+ },
2157
+ ]);
2158
+
2159
+ if (!confirmed) {
2160
+ MessageFormatter.info("Transfer cancelled by user", { prefix: "Transfer" });
2161
+ return;
2162
+ }
2163
+
2164
+ // Password preservation information
2165
+ if (transferOptions.transferTypes.includes("users") && !transferOptions.dryRun) {
2166
+ MessageFormatter.info("User Password Transfer Information:", { prefix: "Transfer" });
2167
+ MessageFormatter.info("✅ Users with hashed passwords (Argon2, Bcrypt, Scrypt, MD5, SHA, PHPass) will preserve their passwords", { prefix: "Transfer" });
2168
+ MessageFormatter.info("⚠️ Users without hash information will receive temporary passwords and need to reset", { prefix: "Transfer" });
2169
+ MessageFormatter.info("🔒 All user data (preferences, labels, verification status) will be preserved", { prefix: "Transfer" });
2170
+
2171
+ const { continueWithUsers } = await inquirer.prompt([
2172
+ {
2173
+ type: "confirm",
2174
+ name: "continueWithUsers",
2175
+ message: "Continue with user transfer?",
2176
+ default: true,
2177
+ },
2178
+ ]);
2179
+
2180
+ if (!continueWithUsers) {
2181
+ // Remove users from transfer types
2182
+ transferOptions.transferTypes = transferOptions.transferTypes.filter((type: string) => type !== "users");
2183
+ if (transferOptions.transferTypes.length === 0) {
2184
+ MessageFormatter.info("No transfer types selected, cancelling", { prefix: "Transfer" });
2185
+ return;
2186
+ }
2187
+ }
2188
+ }
2189
+
2190
+ // Execute comprehensive transfer
2191
+ const comprehensiveTransferOptions: ComprehensiveTransferOptions = {
2192
+ sourceEndpoint: sourceConfig.sourceEndpoint,
2193
+ sourceProject: sourceConfig.sourceProject,
2194
+ sourceKey: sourceConfig.sourceKey,
2195
+ targetEndpoint: targetConfig.targetEndpoint,
2196
+ targetProject: targetConfig.targetProject,
2197
+ targetKey: targetConfig.targetKey,
2198
+ transferUsers: transferOptions.transferTypes.includes("users"),
2199
+ transferDatabases: transferOptions.transferTypes.includes("databases"),
2200
+ transferBuckets: transferOptions.transferTypes.includes("buckets"),
2201
+ transferFunctions: transferOptions.transferTypes.includes("functions"),
2202
+ concurrencyLimit: transferOptions.concurrencyLimit,
2203
+ dryRun: transferOptions.dryRun,
2204
+ };
2205
+
2206
+ const transfer = new ComprehensiveTransfer(comprehensiveTransferOptions);
2207
+ const results = await transfer.execute();
2208
+
2209
+ // Display results
2210
+ if (transferOptions.dryRun) {
2211
+ MessageFormatter.success("Dry run completed successfully!", { prefix: "Transfer" });
2212
+ } else {
2213
+ MessageFormatter.success("Comprehensive transfer completed!", { prefix: "Transfer" });
2214
+ if (transferOptions.transferTypes.includes("users") && results.users.transferred > 0) {
2215
+ MessageFormatter.info("Users with preserved password hashes can log in with their original passwords", { prefix: "Transfer" });
2216
+ MessageFormatter.info("Users with temporary passwords will need to reset their passwords", { prefix: "Transfer" });
2217
+ }
2218
+ }
2219
+
2220
+ } catch (error) {
2221
+ MessageFormatter.error("Comprehensive transfer failed", error instanceof Error ? error : new Error(String(error)), { prefix: "Transfer" });
2222
+ }
2223
+ }
2046
2224
  }