appwrite-utils-cli 1.5.2 → 1.6.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/CHANGELOG.md +199 -0
- package/README.md +251 -29
- package/dist/adapters/AdapterFactory.d.ts +10 -3
- package/dist/adapters/AdapterFactory.js +213 -17
- package/dist/adapters/TablesDBAdapter.js +60 -17
- package/dist/backups/operations/bucketBackup.d.ts +19 -0
- package/dist/backups/operations/bucketBackup.js +197 -0
- package/dist/backups/operations/collectionBackup.d.ts +30 -0
- package/dist/backups/operations/collectionBackup.js +201 -0
- package/dist/backups/operations/comprehensiveBackup.d.ts +25 -0
- package/dist/backups/operations/comprehensiveBackup.js +238 -0
- package/dist/backups/schemas/bucketManifest.d.ts +93 -0
- package/dist/backups/schemas/bucketManifest.js +33 -0
- package/dist/backups/schemas/comprehensiveManifest.d.ts +108 -0
- package/dist/backups/schemas/comprehensiveManifest.js +32 -0
- package/dist/backups/tracking/centralizedTracking.d.ts +34 -0
- package/dist/backups/tracking/centralizedTracking.js +274 -0
- package/dist/cli/commands/configCommands.d.ts +8 -0
- package/dist/cli/commands/configCommands.js +160 -0
- package/dist/cli/commands/databaseCommands.d.ts +13 -0
- package/dist/cli/commands/databaseCommands.js +478 -0
- package/dist/cli/commands/functionCommands.d.ts +7 -0
- package/dist/cli/commands/functionCommands.js +289 -0
- package/dist/cli/commands/schemaCommands.d.ts +7 -0
- package/dist/cli/commands/schemaCommands.js +134 -0
- package/dist/cli/commands/transferCommands.d.ts +5 -0
- package/dist/cli/commands/transferCommands.js +384 -0
- package/dist/collections/attributes.d.ts +5 -4
- package/dist/collections/attributes.js +539 -246
- package/dist/collections/indexes.js +39 -37
- package/dist/collections/methods.d.ts +2 -16
- package/dist/collections/methods.js +90 -538
- package/dist/collections/transferOperations.d.ts +7 -0
- package/dist/collections/transferOperations.js +331 -0
- package/dist/collections/wipeOperations.d.ts +16 -0
- package/dist/collections/wipeOperations.js +328 -0
- package/dist/config/configMigration.d.ts +87 -0
- package/dist/config/configMigration.js +390 -0
- package/dist/config/configValidation.d.ts +66 -0
- package/dist/config/configValidation.js +358 -0
- package/dist/config/yamlConfig.d.ts +455 -1
- package/dist/config/yamlConfig.js +145 -52
- package/dist/databases/methods.js +3 -2
- package/dist/databases/setup.d.ts +1 -2
- package/dist/databases/setup.js +9 -87
- package/dist/examples/yamlTerminologyExample.d.ts +42 -0
- package/dist/examples/yamlTerminologyExample.js +269 -0
- package/dist/functions/deployments.js +11 -10
- package/dist/functions/methods.d.ts +1 -1
- package/dist/functions/methods.js +5 -4
- package/dist/init.js +9 -9
- package/dist/interactiveCLI.d.ts +8 -17
- package/dist/interactiveCLI.js +181 -1172
- package/dist/main.js +364 -21
- package/dist/migrations/afterImportActions.js +22 -30
- package/dist/migrations/appwriteToX.js +71 -25
- package/dist/migrations/dataLoader.js +35 -26
- package/dist/migrations/importController.js +29 -30
- package/dist/migrations/relationships.js +13 -12
- package/dist/migrations/services/ImportOrchestrator.js +16 -19
- package/dist/migrations/transfer.js +46 -46
- package/dist/migrations/yaml/YamlImportConfigLoader.d.ts +3 -1
- package/dist/migrations/yaml/YamlImportConfigLoader.js +6 -3
- package/dist/migrations/yaml/YamlImportIntegration.d.ts +9 -3
- package/dist/migrations/yaml/YamlImportIntegration.js +22 -11
- package/dist/migrations/yaml/generateImportSchemas.d.ts +14 -1
- package/dist/migrations/yaml/generateImportSchemas.js +736 -7
- package/dist/schemas/authUser.d.ts +1 -1
- package/dist/setupController.js +3 -2
- package/dist/shared/backupMetadataSchema.d.ts +94 -0
- package/dist/shared/backupMetadataSchema.js +38 -0
- package/dist/shared/backupTracking.d.ts +18 -0
- package/dist/shared/backupTracking.js +176 -0
- package/dist/shared/confirmationDialogs.js +15 -15
- package/dist/shared/errorUtils.d.ts +54 -0
- package/dist/shared/errorUtils.js +95 -0
- package/dist/shared/functionManager.js +20 -19
- package/dist/shared/indexManager.js +12 -11
- package/dist/shared/jsonSchemaGenerator.js +10 -26
- package/dist/shared/logging.d.ts +51 -0
- package/dist/shared/logging.js +70 -0
- package/dist/shared/messageFormatter.d.ts +2 -0
- package/dist/shared/messageFormatter.js +10 -0
- package/dist/shared/migrationHelpers.d.ts +6 -16
- package/dist/shared/migrationHelpers.js +24 -21
- package/dist/shared/operationLogger.d.ts +8 -1
- package/dist/shared/operationLogger.js +11 -24
- package/dist/shared/operationQueue.d.ts +28 -1
- package/dist/shared/operationQueue.js +268 -66
- package/dist/shared/operationsTable.d.ts +26 -0
- package/dist/shared/operationsTable.js +286 -0
- package/dist/shared/operationsTableSchema.d.ts +48 -0
- package/dist/shared/operationsTableSchema.js +35 -0
- package/dist/shared/relationshipExtractor.d.ts +56 -0
- package/dist/shared/relationshipExtractor.js +138 -0
- package/dist/shared/schemaGenerator.d.ts +19 -1
- package/dist/shared/schemaGenerator.js +56 -75
- package/dist/storage/backupCompression.d.ts +20 -0
- package/dist/storage/backupCompression.js +67 -0
- package/dist/storage/methods.d.ts +16 -2
- package/dist/storage/methods.js +98 -14
- package/dist/users/methods.js +9 -8
- package/dist/utils/configDiscovery.d.ts +78 -0
- package/dist/utils/configDiscovery.js +430 -0
- package/dist/utils/directoryUtils.d.ts +22 -0
- package/dist/utils/directoryUtils.js +59 -0
- package/dist/utils/getClientFromConfig.d.ts +17 -8
- package/dist/utils/getClientFromConfig.js +162 -17
- package/dist/utils/helperFunctions.d.ts +16 -2
- package/dist/utils/helperFunctions.js +19 -5
- package/dist/utils/loadConfigs.d.ts +34 -9
- package/dist/utils/loadConfigs.js +236 -316
- package/dist/utils/pathResolvers.d.ts +53 -0
- package/dist/utils/pathResolvers.js +72 -0
- package/dist/utils/projectConfig.d.ts +119 -0
- package/dist/utils/projectConfig.js +171 -0
- package/dist/utils/retryFailedPromises.js +4 -2
- package/dist/utils/sessionAuth.d.ts +48 -0
- package/dist/utils/sessionAuth.js +164 -0
- package/dist/utils/sessionPreservationExample.d.ts +1666 -0
- package/dist/utils/sessionPreservationExample.js +101 -0
- package/dist/utils/setupFiles.js +301 -41
- package/dist/utils/typeGuards.d.ts +35 -0
- package/dist/utils/typeGuards.js +57 -0
- package/dist/utils/versionDetection.js +145 -9
- package/dist/utils/yamlConverter.d.ts +53 -3
- package/dist/utils/yamlConverter.js +232 -13
- package/dist/utils/yamlLoader.d.ts +70 -0
- package/dist/utils/yamlLoader.js +263 -0
- package/dist/utilsController.d.ts +36 -3
- package/dist/utilsController.js +186 -56
- package/package.json +12 -2
- package/src/adapters/AdapterFactory.ts +263 -35
- package/src/adapters/TablesDBAdapter.ts +225 -36
- package/src/backups/operations/bucketBackup.ts +277 -0
- package/src/backups/operations/collectionBackup.ts +310 -0
- package/src/backups/operations/comprehensiveBackup.ts +342 -0
- package/src/backups/schemas/bucketManifest.ts +78 -0
- package/src/backups/schemas/comprehensiveManifest.ts +76 -0
- package/src/backups/tracking/centralizedTracking.ts +352 -0
- package/src/cli/commands/configCommands.ts +194 -0
- package/src/cli/commands/databaseCommands.ts +635 -0
- package/src/cli/commands/functionCommands.ts +379 -0
- package/src/cli/commands/schemaCommands.ts +163 -0
- package/src/cli/commands/transferCommands.ts +457 -0
- package/src/collections/attributes.ts +900 -621
- package/src/collections/attributes.ts.backup +1555 -0
- package/src/collections/indexes.ts +116 -114
- package/src/collections/methods.ts +295 -968
- package/src/collections/transferOperations.ts +516 -0
- package/src/collections/wipeOperations.ts +501 -0
- package/src/config/README.md +274 -0
- package/src/config/configMigration.ts +575 -0
- package/src/config/configValidation.ts +445 -0
- package/src/config/yamlConfig.ts +168 -55
- package/src/databases/methods.ts +3 -2
- package/src/databases/setup.ts +11 -138
- package/src/examples/yamlTerminologyExample.ts +341 -0
- package/src/functions/deployments.ts +14 -12
- package/src/functions/methods.ts +11 -11
- package/src/functions/templates/hono-typescript/README.md +286 -0
- package/src/functions/templates/hono-typescript/package.json +26 -0
- package/src/functions/templates/hono-typescript/src/adapters/request.ts +74 -0
- package/src/functions/templates/hono-typescript/src/adapters/response.ts +106 -0
- package/src/functions/templates/hono-typescript/src/app.ts +180 -0
- package/src/functions/templates/hono-typescript/src/context.ts +103 -0
- package/src/functions/templates/hono-typescript/src/index.ts +54 -0
- package/src/functions/templates/hono-typescript/src/middleware/appwrite.ts +119 -0
- package/src/functions/templates/hono-typescript/tsconfig.json +20 -0
- package/src/functions/templates/typescript-node/package.json +2 -1
- package/src/functions/templates/typescript-node/src/context.ts +103 -0
- package/src/functions/templates/typescript-node/src/index.ts +18 -12
- package/src/functions/templates/uv/pyproject.toml +1 -0
- package/src/functions/templates/uv/src/context.py +125 -0
- package/src/functions/templates/uv/src/index.py +35 -5
- package/src/init.ts +9 -11
- package/src/interactiveCLI.ts +278 -1596
- package/src/main.ts +418 -24
- package/src/migrations/afterImportActions.ts +71 -44
- package/src/migrations/appwriteToX.ts +100 -34
- package/src/migrations/dataLoader.ts +48 -34
- package/src/migrations/importController.ts +44 -39
- package/src/migrations/relationships.ts +28 -18
- package/src/migrations/services/ImportOrchestrator.ts +24 -27
- package/src/migrations/transfer.ts +159 -121
- package/src/migrations/yaml/YamlImportConfigLoader.ts +11 -4
- package/src/migrations/yaml/YamlImportIntegration.ts +47 -20
- package/src/migrations/yaml/generateImportSchemas.ts +751 -12
- package/src/setupController.ts +3 -2
- package/src/shared/backupMetadataSchema.ts +93 -0
- package/src/shared/backupTracking.ts +211 -0
- package/src/shared/confirmationDialogs.ts +19 -19
- package/src/shared/errorUtils.ts +110 -0
- package/src/shared/functionManager.ts +21 -20
- package/src/shared/indexManager.ts +12 -11
- package/src/shared/jsonSchemaGenerator.ts +38 -52
- package/src/shared/logging.ts +75 -0
- package/src/shared/messageFormatter.ts +14 -1
- package/src/shared/migrationHelpers.ts +45 -38
- package/src/shared/operationLogger.ts +11 -36
- package/src/shared/operationQueue.ts +322 -93
- package/src/shared/operationsTable.ts +338 -0
- package/src/shared/operationsTableSchema.ts +60 -0
- package/src/shared/relationshipExtractor.ts +214 -0
- package/src/shared/schemaGenerator.ts +179 -219
- package/src/storage/backupCompression.ts +88 -0
- package/src/storage/methods.ts +131 -34
- package/src/users/methods.ts +11 -9
- package/src/utils/configDiscovery.ts +502 -0
- package/src/utils/directoryUtils.ts +61 -0
- package/src/utils/getClientFromConfig.ts +205 -22
- package/src/utils/helperFunctions.ts +23 -5
- package/src/utils/loadConfigs.ts +313 -345
- package/src/utils/pathResolvers.ts +81 -0
- package/src/utils/projectConfig.ts +299 -0
- package/src/utils/retryFailedPromises.ts +4 -2
- package/src/utils/sessionAuth.ts +230 -0
- package/src/utils/setupFiles.ts +322 -54
- package/src/utils/typeGuards.ts +65 -0
- package/src/utils/versionDetection.ts +218 -64
- package/src/utils/yamlConverter.ts +296 -13
- package/src/utils/yamlLoader.ts +364 -0
- package/src/utilsController.ts +314 -110
- package/tests/README.md +497 -0
- package/tests/adapters/AdapterFactory.test.ts +277 -0
- package/tests/integration/syncOperations.test.ts +463 -0
- package/tests/jest.config.js +25 -0
- package/tests/migration/configMigration.test.ts +546 -0
- package/tests/setup.ts +62 -0
- package/tests/testUtils.ts +340 -0
- package/tests/utils/loadConfigs.test.ts +350 -0
- package/tests/validation/configValidation.test.ts +412 -0
- package/src/utils/schemaStrings.ts +0 -517
@@ -0,0 +1,457 @@
|
|
1
|
+
import inquirer from "inquirer";
|
2
|
+
import { Databases, Storage } from "node-appwrite";
|
3
|
+
import { MessageFormatter } from "../../shared/messageFormatter.js";
|
4
|
+
import { fetchAllDatabases } from "../../databases/methods.js";
|
5
|
+
import { listBuckets } from "../../storage/methods.js";
|
6
|
+
import { getClient } from "../../utils/getClientFromConfig.js";
|
7
|
+
import { ComprehensiveTransfer, type ComprehensiveTransferOptions } from "../../migrations/comprehensiveTransfer.js";
|
8
|
+
import type { TransferOptions } from "../../migrations/transfer.js";
|
9
|
+
import type { InteractiveCLI } from "../../interactiveCLI.js";
|
10
|
+
|
11
|
+
export const transferCommands = {
|
12
|
+
async transferData(cli: InteractiveCLI): Promise<void> {
|
13
|
+
if (!(cli as any).controller!.database) {
|
14
|
+
throw new Error(
|
15
|
+
"Database is not initialized, is the config file correct & created?"
|
16
|
+
);
|
17
|
+
}
|
18
|
+
|
19
|
+
const { isRemote } = await inquirer.prompt([
|
20
|
+
{
|
21
|
+
type: "confirm",
|
22
|
+
name: "isRemote",
|
23
|
+
message: "Is this a remote transfer?",
|
24
|
+
default: false,
|
25
|
+
},
|
26
|
+
]);
|
27
|
+
|
28
|
+
let sourceClient = (cli as any).controller!.database;
|
29
|
+
let targetClient: Databases;
|
30
|
+
let sourceDatabases: any[];
|
31
|
+
let targetDatabases: any[];
|
32
|
+
let remoteOptions:
|
33
|
+
| {
|
34
|
+
transferEndpoint: string;
|
35
|
+
transferProject: string;
|
36
|
+
transferKey: string;
|
37
|
+
}
|
38
|
+
| undefined;
|
39
|
+
|
40
|
+
if (isRemote) {
|
41
|
+
remoteOptions = await inquirer.prompt([
|
42
|
+
{
|
43
|
+
type: "input",
|
44
|
+
name: "transferEndpoint",
|
45
|
+
message: "Enter the remote endpoint:",
|
46
|
+
},
|
47
|
+
{
|
48
|
+
type: "input",
|
49
|
+
name: "transferProject",
|
50
|
+
message: "Enter the remote project ID:",
|
51
|
+
},
|
52
|
+
{
|
53
|
+
type: "input",
|
54
|
+
name: "transferKey",
|
55
|
+
message: "Enter the remote API key:",
|
56
|
+
},
|
57
|
+
]);
|
58
|
+
|
59
|
+
const remoteClient = getClient(
|
60
|
+
remoteOptions!.transferEndpoint,
|
61
|
+
remoteOptions!.transferProject,
|
62
|
+
remoteOptions!.transferKey
|
63
|
+
);
|
64
|
+
targetClient = new Databases(remoteClient);
|
65
|
+
|
66
|
+
sourceDatabases = await fetchAllDatabases(sourceClient);
|
67
|
+
targetDatabases = await fetchAllDatabases(targetClient);
|
68
|
+
} else {
|
69
|
+
targetClient = sourceClient;
|
70
|
+
const allDatabases = await fetchAllDatabases(sourceClient);
|
71
|
+
sourceDatabases = targetDatabases = allDatabases;
|
72
|
+
}
|
73
|
+
|
74
|
+
const fromDbs = await (cli as any).selectDatabases(
|
75
|
+
sourceDatabases,
|
76
|
+
"Select the source database:",
|
77
|
+
false
|
78
|
+
);
|
79
|
+
const fromDb = fromDbs[0];
|
80
|
+
if (!fromDb) {
|
81
|
+
throw new Error("No source database selected");
|
82
|
+
}
|
83
|
+
const availableDbs = targetDatabases.filter((db: any) => db.$id !== fromDb.$id);
|
84
|
+
const targetDbs = await (cli as any).selectDatabases(
|
85
|
+
availableDbs,
|
86
|
+
"Select the target database:",
|
87
|
+
false
|
88
|
+
);
|
89
|
+
const targetDb = targetDbs[0];
|
90
|
+
if (!targetDb) {
|
91
|
+
throw new Error("No target database selected");
|
92
|
+
}
|
93
|
+
|
94
|
+
const selectedCollections = await (cli as any).selectCollectionsAndTables(
|
95
|
+
fromDb,
|
96
|
+
sourceClient,
|
97
|
+
"Select collections/tables to transfer:",
|
98
|
+
true,
|
99
|
+
false // don't prefer local for transfers
|
100
|
+
);
|
101
|
+
|
102
|
+
const { transferStorage } = await inquirer.prompt([
|
103
|
+
{
|
104
|
+
type: "confirm",
|
105
|
+
name: "transferStorage",
|
106
|
+
message: "Do you want to transfer storage as well?",
|
107
|
+
default: false,
|
108
|
+
},
|
109
|
+
]);
|
110
|
+
|
111
|
+
let sourceBucket, targetBucket;
|
112
|
+
|
113
|
+
if (transferStorage) {
|
114
|
+
const sourceStorage = new Storage((cli as any).controller!.appwriteServer!);
|
115
|
+
const targetStorage = isRemote
|
116
|
+
? new Storage(
|
117
|
+
getClient(
|
118
|
+
remoteOptions!.transferEndpoint,
|
119
|
+
remoteOptions!.transferProject,
|
120
|
+
remoteOptions!.transferKey
|
121
|
+
)
|
122
|
+
)
|
123
|
+
: sourceStorage;
|
124
|
+
|
125
|
+
const sourceBuckets = await listBuckets(sourceStorage);
|
126
|
+
const targetBuckets = isRemote
|
127
|
+
? await listBuckets(targetStorage)
|
128
|
+
: sourceBuckets;
|
129
|
+
|
130
|
+
const sourceBucketPicked = await (cli as any).selectBuckets(
|
131
|
+
sourceBuckets.buckets,
|
132
|
+
"Select the source bucket:",
|
133
|
+
false
|
134
|
+
);
|
135
|
+
const targetBucketPicked = await (cli as any).selectBuckets(
|
136
|
+
targetBuckets.buckets,
|
137
|
+
"Select the target bucket:",
|
138
|
+
false
|
139
|
+
);
|
140
|
+
sourceBucket = sourceBucketPicked[0];
|
141
|
+
targetBucket = targetBucketPicked[0];
|
142
|
+
}
|
143
|
+
|
144
|
+
let transferOptions: TransferOptions = {
|
145
|
+
fromDb,
|
146
|
+
targetDb,
|
147
|
+
isRemote,
|
148
|
+
collections:
|
149
|
+
selectedCollections.length > 0
|
150
|
+
? selectedCollections.map((c: any) => c.$id)
|
151
|
+
: undefined,
|
152
|
+
sourceBucket,
|
153
|
+
targetBucket,
|
154
|
+
};
|
155
|
+
|
156
|
+
if (isRemote && remoteOptions) {
|
157
|
+
transferOptions = {
|
158
|
+
...transferOptions,
|
159
|
+
...remoteOptions,
|
160
|
+
};
|
161
|
+
}
|
162
|
+
|
163
|
+
MessageFormatter.progress("Transferring data...", { prefix: "Transfer" });
|
164
|
+
await (cli as any).controller!.transferData(transferOptions);
|
165
|
+
MessageFormatter.success("Data transfer completed", { prefix: "Transfer" });
|
166
|
+
},
|
167
|
+
|
168
|
+
async comprehensiveTransfer(cli: InteractiveCLI): Promise<void> {
|
169
|
+
MessageFormatter.info("Starting comprehensive transfer configuration...", { prefix: "Transfer" });
|
170
|
+
|
171
|
+
try {
|
172
|
+
// Initialize controller to optionally load config if available (supports both YAML and TypeScript configs)
|
173
|
+
await (cli as any).initControllerIfNeeded();
|
174
|
+
|
175
|
+
// Extract session for potential transfer operations
|
176
|
+
const sessionConfig = (cli as any).extractSessionFromController();
|
177
|
+
|
178
|
+
// Check if user has an appwrite config for easier setup
|
179
|
+
const hasAppwriteConfig = (cli as any).controller?.config?.appwriteEndpoint &&
|
180
|
+
(cli as any).controller?.config?.appwriteProject &&
|
181
|
+
(cli as any).controller?.config?.appwriteKey;
|
182
|
+
|
183
|
+
let sourceConfig: any;
|
184
|
+
let targetConfig: any;
|
185
|
+
|
186
|
+
if (hasAppwriteConfig) {
|
187
|
+
// Offer to use existing config for source
|
188
|
+
const { useConfigForSource } = await inquirer.prompt([
|
189
|
+
{
|
190
|
+
type: "confirm",
|
191
|
+
name: "useConfigForSource",
|
192
|
+
message: "Use your current appwriteConfig as the source?",
|
193
|
+
default: true,
|
194
|
+
},
|
195
|
+
]);
|
196
|
+
|
197
|
+
if (useConfigForSource) {
|
198
|
+
sourceConfig = {
|
199
|
+
sourceEndpoint: (cli as any).controller!.config!.appwriteEndpoint,
|
200
|
+
sourceProject: (cli as any).controller!.config!.appwriteProject,
|
201
|
+
sourceKey: (cli as any).controller!.config!.appwriteKey,
|
202
|
+
};
|
203
|
+
// Preserve session for source if available
|
204
|
+
if (sessionConfig?.sessionCookie) {
|
205
|
+
sourceConfig.sessionCookie = sessionConfig.sessionCookie;
|
206
|
+
sourceConfig.sessionMetadata = sessionConfig.sessionMetadata;
|
207
|
+
}
|
208
|
+
MessageFormatter.info(`Using config source: ${sourceConfig.sourceEndpoint}`, { prefix: "Transfer" });
|
209
|
+
} else {
|
210
|
+
// Get source configuration manually
|
211
|
+
sourceConfig = await inquirer.prompt([
|
212
|
+
{
|
213
|
+
type: "input",
|
214
|
+
name: "sourceEndpoint",
|
215
|
+
message: "Enter the source Appwrite endpoint:",
|
216
|
+
validate: (input) => input.trim() !== "" || "Endpoint cannot be empty",
|
217
|
+
},
|
218
|
+
{
|
219
|
+
type: "input",
|
220
|
+
name: "sourceProject",
|
221
|
+
message: "Enter the source project ID:",
|
222
|
+
validate: (input) => input.trim() !== "" || "Project ID cannot be empty",
|
223
|
+
},
|
224
|
+
{
|
225
|
+
type: "password",
|
226
|
+
name: "sourceKey",
|
227
|
+
message: "Enter the source API key:",
|
228
|
+
validate: (input) => input.trim() !== "" || "API key cannot be empty",
|
229
|
+
},
|
230
|
+
]);
|
231
|
+
}
|
232
|
+
|
233
|
+
// Offer to use existing config for target
|
234
|
+
const { useConfigForTarget } = await inquirer.prompt([
|
235
|
+
{
|
236
|
+
type: "confirm",
|
237
|
+
name: "useConfigForTarget",
|
238
|
+
message: "Use your current appwriteConfig as the target?",
|
239
|
+
default: false,
|
240
|
+
},
|
241
|
+
]);
|
242
|
+
|
243
|
+
if (useConfigForTarget) {
|
244
|
+
targetConfig = {
|
245
|
+
targetEndpoint: (cli as any).controller!.config!.appwriteEndpoint,
|
246
|
+
targetProject: (cli as any).controller!.config!.appwriteProject,
|
247
|
+
targetKey: (cli as any).controller!.config!.appwriteKey,
|
248
|
+
};
|
249
|
+
// Preserve session for target if available
|
250
|
+
if (sessionConfig?.sessionCookie) {
|
251
|
+
targetConfig.sessionCookie = sessionConfig.sessionCookie;
|
252
|
+
targetConfig.sessionMetadata = sessionConfig.sessionMetadata;
|
253
|
+
}
|
254
|
+
MessageFormatter.info(`Using config target: ${targetConfig.targetEndpoint}`, { prefix: "Transfer" });
|
255
|
+
} else {
|
256
|
+
// Get target configuration manually
|
257
|
+
targetConfig = await inquirer.prompt([
|
258
|
+
{
|
259
|
+
type: "input",
|
260
|
+
name: "targetEndpoint",
|
261
|
+
message: "Enter the target Appwrite endpoint:",
|
262
|
+
validate: (input) => input.trim() !== "" || "Endpoint cannot be empty",
|
263
|
+
},
|
264
|
+
{
|
265
|
+
type: "input",
|
266
|
+
name: "targetProject",
|
267
|
+
message: "Enter the target project ID:",
|
268
|
+
validate: (input) => input.trim() !== "" || "Project ID cannot be empty",
|
269
|
+
},
|
270
|
+
{
|
271
|
+
type: "password",
|
272
|
+
name: "targetKey",
|
273
|
+
message: "Enter the target API key:",
|
274
|
+
validate: (input) => input.trim() !== "" || "API key cannot be empty",
|
275
|
+
},
|
276
|
+
]);
|
277
|
+
}
|
278
|
+
} else {
|
279
|
+
// No appwrite config found, get both configurations manually
|
280
|
+
MessageFormatter.info("No appwriteConfig found, please enter source and target configurations manually", { prefix: "Transfer" });
|
281
|
+
|
282
|
+
// Get source configuration
|
283
|
+
sourceConfig = await inquirer.prompt([
|
284
|
+
{
|
285
|
+
type: "input",
|
286
|
+
name: "sourceEndpoint",
|
287
|
+
message: "Enter the source Appwrite endpoint:",
|
288
|
+
validate: (input) => input.trim() !== "" || "Endpoint cannot be empty",
|
289
|
+
},
|
290
|
+
{
|
291
|
+
type: "input",
|
292
|
+
name: "sourceProject",
|
293
|
+
message: "Enter the source project ID:",
|
294
|
+
validate: (input) => input.trim() !== "" || "Project ID cannot be empty",
|
295
|
+
},
|
296
|
+
{
|
297
|
+
type: "password",
|
298
|
+
name: "sourceKey",
|
299
|
+
message: "Enter the source API key:",
|
300
|
+
validate: (input) => input.trim() !== "" || "API key cannot be empty",
|
301
|
+
},
|
302
|
+
]);
|
303
|
+
|
304
|
+
// Get target configuration
|
305
|
+
targetConfig = await inquirer.prompt([
|
306
|
+
{
|
307
|
+
type: "input",
|
308
|
+
name: "targetEndpoint",
|
309
|
+
message: "Enter the target Appwrite endpoint:",
|
310
|
+
validate: (input) => input.trim() !== "" || "Endpoint cannot be empty",
|
311
|
+
},
|
312
|
+
{
|
313
|
+
type: "input",
|
314
|
+
name: "targetProject",
|
315
|
+
message: "Enter the target project ID:",
|
316
|
+
validate: (input) => input.trim() !== "" || "Project ID cannot be empty",
|
317
|
+
},
|
318
|
+
{
|
319
|
+
type: "password",
|
320
|
+
name: "targetKey",
|
321
|
+
message: "Enter the target API key:",
|
322
|
+
validate: (input) => input.trim() !== "" || "API key cannot be empty",
|
323
|
+
},
|
324
|
+
]);
|
325
|
+
}
|
326
|
+
|
327
|
+
// Get transfer options
|
328
|
+
const transferOptions = await inquirer.prompt([
|
329
|
+
{
|
330
|
+
type: "checkbox",
|
331
|
+
name: "transferTypes",
|
332
|
+
message: "Select what to transfer:",
|
333
|
+
choices: [
|
334
|
+
{ name: "👥 Users", value: "users", checked: true },
|
335
|
+
{ name: "👥 Teams", value: "teams", checked: true },
|
336
|
+
{ name: "🗄️ Databases", value: "databases", checked: true },
|
337
|
+
{ name: "📦 Storage Buckets", value: "buckets", checked: true },
|
338
|
+
{ name: "⚡ Functions", value: "functions", checked: true },
|
339
|
+
],
|
340
|
+
validate: (input) => input.length > 0 || "Select at least one transfer type",
|
341
|
+
},
|
342
|
+
{
|
343
|
+
type: "list",
|
344
|
+
name: "concurrencyLimit",
|
345
|
+
message: "Select concurrency limit:",
|
346
|
+
choices: [
|
347
|
+
{ name: "5 (Conservative) - Users: 2, Files: 1", value: 5 },
|
348
|
+
{ name: "10 (Balanced) - Users: 5, Files: 2", value: 10 },
|
349
|
+
{ name: "15 - Users: 7, Files: 3", value: 15 },
|
350
|
+
{ name: "20 - Users: 10, Files: 5", value: 20 },
|
351
|
+
{ name: "25 - Users: 12, Files: 6", value: 25 },
|
352
|
+
{ name: "30 - Users: 15, Files: 7", value: 30 },
|
353
|
+
{ name: "35 - Users: 17, Files: 8", value: 35 },
|
354
|
+
{ name: "40 - Users: 20, Files: 10", value: 40 },
|
355
|
+
{ name: "45 - Users: 22, Files: 11", value: 45 },
|
356
|
+
{ name: "50 - Users: 25, Files: 12", value: 50 },
|
357
|
+
{ name: "55 - Users: 27, Files: 13", value: 55 },
|
358
|
+
{ name: "60 - Users: 30, Files: 15", value: 60 },
|
359
|
+
{ name: "65 - Users: 32, Files: 16", value: 65 },
|
360
|
+
{ name: "70 - Users: 35, Files: 17", value: 70 },
|
361
|
+
{ name: "75 - Users: 37, Files: 18", value: 75 },
|
362
|
+
{ name: "80 - Users: 40, Files: 20", value: 80 },
|
363
|
+
{ name: "85 - Users: 42, Files: 21", value: 85 },
|
364
|
+
{ name: "90 - Users: 45, Files: 22", value: 90 },
|
365
|
+
{ name: "95 - Users: 47, Files: 23", value: 95 },
|
366
|
+
{ name: "100 (Aggressive) - Users: 50, Files: 25", value: 100 },
|
367
|
+
],
|
368
|
+
default: 10,
|
369
|
+
},
|
370
|
+
{
|
371
|
+
type: "confirm",
|
372
|
+
name: "dryRun",
|
373
|
+
message: "Run in dry-run mode (no actual changes)?",
|
374
|
+
default: false,
|
375
|
+
},
|
376
|
+
]);
|
377
|
+
|
378
|
+
// Confirmation
|
379
|
+
const { confirmed } = await inquirer.prompt([
|
380
|
+
{
|
381
|
+
type: "confirm",
|
382
|
+
name: "confirmed",
|
383
|
+
message: `Are you sure you want to ${transferOptions.dryRun ? "dry-run" : "perform"} comprehensive transfer from ${sourceConfig.sourceEndpoint} to ${targetConfig.targetEndpoint}?`,
|
384
|
+
default: false,
|
385
|
+
},
|
386
|
+
]);
|
387
|
+
|
388
|
+
if (!confirmed) {
|
389
|
+
MessageFormatter.info("Transfer cancelled by user", { prefix: "Transfer" });
|
390
|
+
return;
|
391
|
+
}
|
392
|
+
|
393
|
+
// Password preservation information
|
394
|
+
if (transferOptions.transferTypes.includes("users") && !transferOptions.dryRun) {
|
395
|
+
MessageFormatter.info("User Password Transfer Information:", { prefix: "Transfer" });
|
396
|
+
MessageFormatter.info("✅ Users with hashed passwords (Argon2, Bcrypt, Scrypt, MD5, SHA, PHPass) will preserve their passwords", { prefix: "Transfer" });
|
397
|
+
MessageFormatter.info("⚠️ Users without hash information will receive temporary passwords and need to reset", { prefix: "Transfer" });
|
398
|
+
MessageFormatter.info("🔒 All user data (preferences, labels, verification status) will be preserved", { prefix: "Transfer" });
|
399
|
+
|
400
|
+
const { continueWithUsers } = await inquirer.prompt([
|
401
|
+
{
|
402
|
+
type: "confirm",
|
403
|
+
name: "continueWithUsers",
|
404
|
+
message: "Continue with user transfer?",
|
405
|
+
default: true,
|
406
|
+
},
|
407
|
+
]);
|
408
|
+
|
409
|
+
if (!continueWithUsers) {
|
410
|
+
// Remove users from transfer types
|
411
|
+
transferOptions.transferTypes = transferOptions.transferTypes.filter((type: string) => type !== "users");
|
412
|
+
if (transferOptions.transferTypes.length === 0) {
|
413
|
+
MessageFormatter.info("No transfer types selected, cancelling", { prefix: "Transfer" });
|
414
|
+
return;
|
415
|
+
}
|
416
|
+
}
|
417
|
+
}
|
418
|
+
|
419
|
+
// Execute comprehensive transfer
|
420
|
+
const comprehensiveTransferOptions: ComprehensiveTransferOptions = {
|
421
|
+
sourceEndpoint: sourceConfig.sourceEndpoint,
|
422
|
+
sourceProject: sourceConfig.sourceProject,
|
423
|
+
sourceKey: sourceConfig.sourceKey,
|
424
|
+
targetEndpoint: targetConfig.targetEndpoint,
|
425
|
+
targetProject: targetConfig.targetProject,
|
426
|
+
targetKey: targetConfig.targetKey,
|
427
|
+
transferUsers: transferOptions.transferTypes.includes("users"),
|
428
|
+
transferTeams: transferOptions.transferTypes.includes("teams"),
|
429
|
+
transferDatabases: transferOptions.transferTypes.includes("databases"),
|
430
|
+
transferBuckets: transferOptions.transferTypes.includes("buckets"),
|
431
|
+
transferFunctions: transferOptions.transferTypes.includes("functions"),
|
432
|
+
concurrencyLimit: transferOptions.concurrencyLimit,
|
433
|
+
dryRun: transferOptions.dryRun,
|
434
|
+
};
|
435
|
+
|
436
|
+
const transfer = new ComprehensiveTransfer(comprehensiveTransferOptions);
|
437
|
+
const results = await transfer.execute();
|
438
|
+
|
439
|
+
// Display results
|
440
|
+
if (transferOptions.dryRun) {
|
441
|
+
MessageFormatter.success("Dry run completed successfully!", { prefix: "Transfer" });
|
442
|
+
} else {
|
443
|
+
MessageFormatter.success("Comprehensive transfer completed!", { prefix: "Transfer" });
|
444
|
+
if (transferOptions.transferTypes.includes("users") && results.users.transferred > 0) {
|
445
|
+
MessageFormatter.info("Users with preserved password hashes can log in with their original passwords", { prefix: "Transfer" });
|
446
|
+
MessageFormatter.info("Users with temporary passwords will need to reset their passwords", { prefix: "Transfer" });
|
447
|
+
}
|
448
|
+
if (transferOptions.transferTypes.includes("teams") && results.teams.transferred > 0) {
|
449
|
+
MessageFormatter.info("Team memberships have been transferred and may require user acceptance of invitations", { prefix: "Transfer" });
|
450
|
+
}
|
451
|
+
}
|
452
|
+
|
453
|
+
} catch (error) {
|
454
|
+
MessageFormatter.error("Comprehensive transfer failed", error instanceof Error ? error : new Error(String(error)), { prefix: "Transfer" });
|
455
|
+
}
|
456
|
+
}
|
457
|
+
};
|