appwrite-utils-cli 1.0.9 → 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.
@@ -2052,49 +2052,144 @@ export class InteractiveCLI {
2052
2052
  MessageFormatter.info("Starting comprehensive transfer configuration...", { prefix: "Transfer" });
2053
2053
 
2054
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
- ]);
2055
+ // Check if user has an appwrite config for easier setup
2056
+ const hasAppwriteConfig = this.controller?.config?.appwriteEndpoint &&
2057
+ this.controller?.config?.appwriteProject &&
2058
+ this.controller?.config?.appwriteKey;
2076
2059
 
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
- ]);
2060
+ let sourceConfig: any;
2061
+ let targetConfig: any;
2062
+
2063
+ if (hasAppwriteConfig) {
2064
+ // Offer to use existing config for source
2065
+ const { useConfigForSource } = await inquirer.prompt([
2066
+ {
2067
+ type: "confirm",
2068
+ name: "useConfigForSource",
2069
+ message: "Use your current appwriteConfig as the source?",
2070
+ default: true,
2071
+ },
2072
+ ]);
2073
+
2074
+ if (useConfigForSource) {
2075
+ sourceConfig = {
2076
+ sourceEndpoint: this.controller!.config!.appwriteEndpoint,
2077
+ sourceProject: this.controller!.config!.appwriteProject,
2078
+ sourceKey: this.controller!.config!.appwriteKey,
2079
+ };
2080
+ MessageFormatter.info(`Using config source: ${sourceConfig.sourceEndpoint}`, { prefix: "Transfer" });
2081
+ } else {
2082
+ // Get source configuration manually
2083
+ sourceConfig = await inquirer.prompt([
2084
+ {
2085
+ type: "input",
2086
+ name: "sourceEndpoint",
2087
+ message: "Enter the source Appwrite endpoint:",
2088
+ validate: (input) => input.trim() !== "" || "Endpoint cannot be empty",
2089
+ },
2090
+ {
2091
+ type: "input",
2092
+ name: "sourceProject",
2093
+ message: "Enter the source project ID:",
2094
+ validate: (input) => input.trim() !== "" || "Project ID cannot be empty",
2095
+ },
2096
+ {
2097
+ type: "password",
2098
+ name: "sourceKey",
2099
+ message: "Enter the source API key:",
2100
+ validate: (input) => input.trim() !== "" || "API key cannot be empty",
2101
+ },
2102
+ ]);
2103
+ }
2104
+
2105
+ // Offer to use existing config for target
2106
+ const { useConfigForTarget } = await inquirer.prompt([
2107
+ {
2108
+ type: "confirm",
2109
+ name: "useConfigForTarget",
2110
+ message: "Use your current appwriteConfig as the target?",
2111
+ default: false,
2112
+ },
2113
+ ]);
2114
+
2115
+ if (useConfigForTarget) {
2116
+ targetConfig = {
2117
+ targetEndpoint: this.controller!.config!.appwriteEndpoint,
2118
+ targetProject: this.controller!.config!.appwriteProject,
2119
+ targetKey: this.controller!.config!.appwriteKey,
2120
+ };
2121
+ MessageFormatter.info(`Using config target: ${targetConfig.targetEndpoint}`, { prefix: "Transfer" });
2122
+ } else {
2123
+ // Get target configuration manually
2124
+ targetConfig = await inquirer.prompt([
2125
+ {
2126
+ type: "input",
2127
+ name: "targetEndpoint",
2128
+ message: "Enter the target Appwrite endpoint:",
2129
+ validate: (input) => input.trim() !== "" || "Endpoint cannot be empty",
2130
+ },
2131
+ {
2132
+ type: "input",
2133
+ name: "targetProject",
2134
+ message: "Enter the target project ID:",
2135
+ validate: (input) => input.trim() !== "" || "Project ID cannot be empty",
2136
+ },
2137
+ {
2138
+ type: "password",
2139
+ name: "targetKey",
2140
+ message: "Enter the target API key:",
2141
+ validate: (input) => input.trim() !== "" || "API key cannot be empty",
2142
+ },
2143
+ ]);
2144
+ }
2145
+ } else {
2146
+ // No appwrite config found, get both configurations manually
2147
+ MessageFormatter.info("No appwriteConfig found, please enter source and target configurations manually", { prefix: "Transfer" });
2148
+
2149
+ // Get source configuration
2150
+ sourceConfig = await inquirer.prompt([
2151
+ {
2152
+ type: "input",
2153
+ name: "sourceEndpoint",
2154
+ message: "Enter the source Appwrite endpoint:",
2155
+ validate: (input) => input.trim() !== "" || "Endpoint cannot be empty",
2156
+ },
2157
+ {
2158
+ type: "input",
2159
+ name: "sourceProject",
2160
+ message: "Enter the source project ID:",
2161
+ validate: (input) => input.trim() !== "" || "Project ID cannot be empty",
2162
+ },
2163
+ {
2164
+ type: "password",
2165
+ name: "sourceKey",
2166
+ message: "Enter the source API key:",
2167
+ validate: (input) => input.trim() !== "" || "API key cannot be empty",
2168
+ },
2169
+ ]);
2170
+
2171
+ // Get target configuration
2172
+ targetConfig = await inquirer.prompt([
2173
+ {
2174
+ type: "input",
2175
+ name: "targetEndpoint",
2176
+ message: "Enter the target Appwrite endpoint:",
2177
+ validate: (input) => input.trim() !== "" || "Endpoint cannot be empty",
2178
+ },
2179
+ {
2180
+ type: "input",
2181
+ name: "targetProject",
2182
+ message: "Enter the target project ID:",
2183
+ validate: (input) => input.trim() !== "" || "Project ID cannot be empty",
2184
+ },
2185
+ {
2186
+ type: "password",
2187
+ name: "targetKey",
2188
+ message: "Enter the target API key:",
2189
+ validate: (input) => input.trim() !== "" || "API key cannot be empty",
2190
+ },
2191
+ ]);
2192
+ }
2098
2193
 
2099
2194
  // Get transfer options
2100
2195
  const transferOptions = await inquirer.prompt([
@@ -13,6 +13,7 @@ import { getAppwriteClient } from "../utils/helperFunctions.js";
13
13
  import {
14
14
  createOrUpdateAttribute,
15
15
  createUpdateCollectionAttributes,
16
+ createUpdateCollectionAttributesWithStatusCheck,
16
17
  } from "../collections/attributes.js";
17
18
  import { parseAttribute } from "appwrite-utils";
18
19
  import chalk from "chalk";
@@ -22,6 +23,7 @@ import { ProgressManager } from "../shared/progressManager.js";
22
23
  import {
23
24
  createOrUpdateIndex,
24
25
  createOrUpdateIndexes,
26
+ createOrUpdateIndexesWithStatusCheck,
25
27
  } from "../collections/indexes.js";
26
28
  import { getClient } from "../utils/getClientFromConfig.js";
27
29
 
@@ -305,44 +307,23 @@ export const transferDatabaseLocalToLocal = async (
305
307
  );
306
308
  }
307
309
 
308
- // Handle attributes
309
- const existingAttributes = await tryAwaitWithRetry(
310
- async () =>
311
- await localDb.listAttributes(targetDbId, targetCollection.$id)
310
+ // Handle attributes with enhanced status checking
311
+ console.log(chalk.blue(`Creating attributes for collection ${collection.name} with enhanced monitoring...`));
312
+
313
+ const allAttributes = collection.attributes.map(attr => parseAttribute(attr as any));
314
+ const attributeSuccess = await createUpdateCollectionAttributesWithStatusCheck(
315
+ localDb,
316
+ targetDbId,
317
+ targetCollection,
318
+ allAttributes
312
319
  );
313
-
314
- for (const attribute of collection.attributes) {
315
- const parsedAttribute = parseAttribute(attribute as any);
316
- const existingAttribute = existingAttributes.attributes.find(
317
- (attr: any) => attr.key === parsedAttribute.key
318
- );
319
-
320
- if (!existingAttribute) {
321
- await tryAwaitWithRetry(async () =>
322
- createOrUpdateAttribute(
323
- localDb,
324
- targetDbId,
325
- targetCollection,
326
- parsedAttribute
327
- )
328
- );
329
- console.log(chalk.green(`Attribute ${parsedAttribute.key} created`));
330
- } else {
331
- console.log(
332
- chalk.blue(
333
- `Attribute ${parsedAttribute.key} exists, checking for updates...`
334
- )
335
- );
336
- await tryAwaitWithRetry(async () =>
337
- createOrUpdateAttribute(
338
- localDb,
339
- targetDbId,
340
- targetCollection,
341
- parsedAttribute
342
- )
343
- );
344
- }
320
+
321
+ if (!attributeSuccess) {
322
+ console.log(chalk.red(`❌ Failed to create all attributes for collection ${collection.name}, skipping to next collection`));
323
+ continue;
345
324
  }
325
+
326
+ console.log(chalk.green(`✅ All attributes created successfully for collection ${collection.name}`));
346
327
 
347
328
  // Handle indexes
348
329
  const existingIndexes = await tryAwaitWithRetry(
@@ -474,73 +455,41 @@ export const transferDatabaseLocalToRemote = async (
474
455
  );
475
456
  }
476
457
 
477
- // Handle attributes
478
- const existingAttributes = await tryAwaitWithRetry(
479
- async () => await remoteDb.listAttributes(toDbId, targetCollection.$id)
458
+ // Handle attributes with enhanced status checking
459
+ console.log(chalk.blue(`Creating attributes for collection ${collection.name} with enhanced monitoring...`));
460
+
461
+ const attributesToCreate = collection.attributes.map(attr => parseAttribute(attr as any));
462
+
463
+ const attributesSuccess = await createUpdateCollectionAttributesWithStatusCheck(
464
+ remoteDb,
465
+ toDbId,
466
+ targetCollection,
467
+ attributesToCreate
480
468
  );
481
-
482
- for (const attribute of collection.attributes) {
483
- const parsedAttribute = parseAttribute(attribute as any);
484
- const existingAttribute = existingAttributes.attributes.find(
485
- (attr: any) => attr.key === parsedAttribute.key
486
- );
487
-
488
- if (!existingAttribute) {
489
- await tryAwaitWithRetry(async () =>
490
- createOrUpdateAttribute(
491
- remoteDb,
492
- toDbId,
493
- targetCollection,
494
- parsedAttribute
495
- )
496
- );
497
- console.log(chalk.green(`Attribute ${parsedAttribute.key} created`));
498
- } else {
499
- console.log(
500
- chalk.blue(
501
- `Attribute ${parsedAttribute.key} exists, checking for updates...`
502
- )
503
- );
504
- await tryAwaitWithRetry(async () =>
505
- createOrUpdateAttribute(
506
- remoteDb,
507
- toDbId,
508
- targetCollection,
509
- parsedAttribute
510
- )
511
- );
512
- }
469
+
470
+ if (!attributesSuccess) {
471
+ console.log(chalk.red(`Failed to create some attributes for collection ${collection.name}`));
472
+ // Continue with the transfer even if some attributes failed
473
+ } else {
474
+ console.log(chalk.green(`All attributes created successfully for collection ${collection.name}`));
513
475
  }
514
476
 
515
- // Handle indexes
516
- const existingIndexes = await tryAwaitWithRetry(
517
- async () => await remoteDb.listIndexes(toDbId, targetCollection.$id)
477
+ // Handle indexes with enhanced status checking
478
+ console.log(chalk.blue(`Creating indexes for collection ${collection.name} with enhanced monitoring...`));
479
+
480
+ const indexesSuccess = await createOrUpdateIndexesWithStatusCheck(
481
+ toDbId,
482
+ remoteDb,
483
+ targetCollection.$id,
484
+ targetCollection,
485
+ collection.indexes as any
518
486
  );
519
-
520
- for (const index of collection.indexes) {
521
- const existingIndex = existingIndexes.indexes.find(
522
- (idx) => idx.key === index.key
523
- );
524
-
525
- if (!existingIndex) {
526
- await createOrUpdateIndex(
527
- toDbId,
528
- remoteDb,
529
- targetCollection.$id,
530
- index as any
531
- );
532
- console.log(chalk.green(`Index ${index.key} created`));
533
- } else {
534
- console.log(
535
- chalk.blue(`Index ${index.key} exists, checking for updates...`)
536
- );
537
- await createOrUpdateIndex(
538
- toDbId,
539
- remoteDb,
540
- targetCollection.$id,
541
- index as any
542
- );
543
- }
487
+
488
+ if (!indexesSuccess) {
489
+ console.log(chalk.red(`Failed to create some indexes for collection ${collection.name}`));
490
+ // Continue with the transfer even if some indexes failed
491
+ } else {
492
+ console.log(chalk.green(`All indexes created successfully for collection ${collection.name}`));
544
493
  }
545
494
 
546
495
  // Transfer documents