appwrite-utils-cli 0.0.286 → 0.9.2
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 +162 -96
- package/dist/collections/attributes.d.ts +4 -0
- package/dist/collections/attributes.js +224 -0
- package/dist/collections/indexes.d.ts +4 -0
- package/dist/collections/indexes.js +27 -0
- package/dist/collections/methods.d.ts +16 -0
- package/dist/collections/methods.js +216 -0
- package/dist/databases/methods.d.ts +6 -0
- package/dist/databases/methods.js +33 -0
- package/dist/interactiveCLI.d.ts +19 -0
- package/dist/interactiveCLI.js +555 -0
- package/dist/main.js +224 -62
- package/dist/migrations/afterImportActions.js +37 -40
- package/dist/migrations/appwriteToX.d.ts +26 -25
- package/dist/migrations/appwriteToX.js +42 -6
- package/dist/migrations/attributes.js +21 -20
- package/dist/migrations/backup.d.ts +93 -87
- package/dist/migrations/collections.d.ts +6 -0
- package/dist/migrations/collections.js +149 -20
- package/dist/migrations/converters.d.ts +2 -18
- package/dist/migrations/converters.js +13 -2
- package/dist/migrations/dataLoader.d.ts +276 -161
- package/dist/migrations/dataLoader.js +535 -292
- package/dist/migrations/databases.js +8 -2
- package/dist/migrations/helper.d.ts +3 -0
- package/dist/migrations/helper.js +21 -0
- package/dist/migrations/importController.d.ts +5 -2
- package/dist/migrations/importController.js +125 -88
- package/dist/migrations/importDataActions.d.ts +9 -1
- package/dist/migrations/importDataActions.js +15 -3
- package/dist/migrations/indexes.js +3 -2
- package/dist/migrations/logging.js +20 -8
- package/dist/migrations/migrationHelper.d.ts +9 -4
- package/dist/migrations/migrationHelper.js +6 -5
- package/dist/migrations/openapi.d.ts +1 -1
- package/dist/migrations/openapi.js +33 -18
- package/dist/migrations/queue.js +3 -2
- package/dist/migrations/relationships.d.ts +2 -2
- package/dist/migrations/schemaStrings.js +53 -41
- package/dist/migrations/setupDatabase.d.ts +2 -4
- package/dist/migrations/setupDatabase.js +24 -105
- package/dist/migrations/storage.d.ts +3 -1
- package/dist/migrations/storage.js +110 -16
- package/dist/migrations/transfer.d.ts +30 -0
- package/dist/migrations/transfer.js +337 -0
- package/dist/migrations/users.d.ts +2 -1
- package/dist/migrations/users.js +78 -43
- package/dist/schemas/authUser.d.ts +2 -2
- package/dist/storage/methods.d.ts +15 -0
- package/dist/storage/methods.js +207 -0
- package/dist/storage/schemas.d.ts +687 -0
- package/dist/storage/schemas.js +175 -0
- package/dist/utils/getClientFromConfig.d.ts +4 -0
- package/dist/utils/getClientFromConfig.js +16 -0
- package/dist/utils/helperFunctions.d.ts +11 -1
- package/dist/utils/helperFunctions.js +38 -0
- package/dist/utils/retryFailedPromises.d.ts +2 -0
- package/dist/utils/retryFailedPromises.js +21 -0
- package/dist/utils/schemaStrings.d.ts +13 -0
- package/dist/utils/schemaStrings.js +403 -0
- package/dist/utils/setupFiles.js +110 -61
- package/dist/utilsController.d.ts +40 -22
- package/dist/utilsController.js +164 -84
- package/package.json +13 -15
- package/src/collections/attributes.ts +483 -0
- package/src/collections/indexes.ts +53 -0
- package/src/collections/methods.ts +331 -0
- package/src/databases/methods.ts +47 -0
- package/src/init.ts +64 -64
- package/src/interactiveCLI.ts +767 -0
- package/src/main.ts +289 -83
- package/src/migrations/afterImportActions.ts +553 -490
- package/src/migrations/appwriteToX.ts +237 -174
- package/src/migrations/attributes.ts +483 -422
- package/src/migrations/backup.ts +205 -205
- package/src/migrations/collections.ts +545 -300
- package/src/migrations/converters.ts +161 -150
- package/src/migrations/dataLoader.ts +1615 -1304
- package/src/migrations/databases.ts +44 -25
- package/src/migrations/dbHelpers.ts +92 -92
- package/src/migrations/helper.ts +40 -0
- package/src/migrations/importController.ts +448 -384
- package/src/migrations/importDataActions.ts +315 -307
- package/src/migrations/indexes.ts +40 -37
- package/src/migrations/logging.ts +29 -16
- package/src/migrations/migrationHelper.ts +207 -201
- package/src/migrations/openapi.ts +83 -70
- package/src/migrations/queue.ts +118 -119
- package/src/migrations/relationships.ts +324 -324
- package/src/migrations/schemaStrings.ts +472 -460
- package/src/migrations/setupDatabase.ts +118 -219
- package/src/migrations/storage.ts +538 -358
- package/src/migrations/transfer.ts +608 -0
- package/src/migrations/users.ts +362 -285
- package/src/migrations/validationRules.ts +63 -63
- package/src/schemas/authUser.ts +23 -23
- package/src/setup.ts +8 -8
- package/src/storage/methods.ts +371 -0
- package/src/storage/schemas.ts +205 -0
- package/src/types.ts +9 -9
- package/src/utils/getClientFromConfig.ts +17 -0
- package/src/utils/helperFunctions.ts +181 -127
- package/src/utils/index.ts +2 -2
- package/src/utils/loadConfigs.ts +59 -59
- package/src/utils/retryFailedPromises.ts +27 -0
- package/src/utils/schemaStrings.ts +473 -0
- package/src/utils/setupFiles.ts +228 -182
- package/src/utilsController.ts +325 -194
- package/tsconfig.json +37 -37
@@ -0,0 +1,555 @@
|
|
1
|
+
import inquirer from "inquirer";
|
2
|
+
import { UtilsController } from "./utilsController.js";
|
3
|
+
import { createEmptyCollection, setupDirsFiles } from "./utils/setupFiles.js";
|
4
|
+
import { fetchAllDatabases } from "./databases/methods.js";
|
5
|
+
import { fetchAllCollections } from "./collections/methods.js";
|
6
|
+
import { listBuckets, createBucket } from "./storage/methods.js";
|
7
|
+
import { Databases, Storage, Client, Compression, } from "node-appwrite";
|
8
|
+
import { getClient } from "./utils/getClientFromConfig.js";
|
9
|
+
import { ulid } from "ulidx";
|
10
|
+
var CHOICES;
|
11
|
+
(function (CHOICES) {
|
12
|
+
CHOICES["CREATE_COLLECTION_CONFIG"] = "Create collection config file";
|
13
|
+
CHOICES["SETUP_DIRS_FILES"] = "Setup directories and files";
|
14
|
+
CHOICES["SETUP_DIRS_FILES_WITH_EXAMPLE_DATA"] = "Setup directories and files with example data";
|
15
|
+
CHOICES["SYNC_DB"] = "Push local config to Appwrite";
|
16
|
+
CHOICES["SYNCHRONIZE_CONFIGURATIONS"] = "Synchronize configurations";
|
17
|
+
CHOICES["TRANSFER_DATA"] = "Transfer data";
|
18
|
+
CHOICES["BACKUP_DATABASE"] = "Backup database";
|
19
|
+
CHOICES["WIPE_DATABASE"] = "Wipe database";
|
20
|
+
CHOICES["GENERATE_SCHEMAS"] = "Generate schemas";
|
21
|
+
CHOICES["IMPORT_DATA"] = "Import data";
|
22
|
+
CHOICES["EXIT"] = "Exit";
|
23
|
+
})(CHOICES || (CHOICES = {}));
|
24
|
+
export class InteractiveCLI {
|
25
|
+
controller;
|
26
|
+
constructor(currentDir, utilsController) {
|
27
|
+
if (utilsController) {
|
28
|
+
this.controller = utilsController;
|
29
|
+
}
|
30
|
+
else if (currentDir) {
|
31
|
+
this.controller = new UtilsController(currentDir);
|
32
|
+
}
|
33
|
+
else {
|
34
|
+
throw new Error("Current directory or utils controller is required");
|
35
|
+
}
|
36
|
+
}
|
37
|
+
async run() {
|
38
|
+
console.log("Welcome to Appwrite Utils CLI Tool by Zach Handley");
|
39
|
+
console.log("For more information, visit https://github.com/zachhandley/appwrite-utils");
|
40
|
+
await this.controller.init();
|
41
|
+
while (true) {
|
42
|
+
const { action } = await inquirer.prompt([
|
43
|
+
{
|
44
|
+
type: "list",
|
45
|
+
name: "action",
|
46
|
+
message: "What would you like to do?",
|
47
|
+
choices: Object.values(CHOICES),
|
48
|
+
},
|
49
|
+
]);
|
50
|
+
switch (action) {
|
51
|
+
case CHOICES.CREATE_COLLECTION_CONFIG:
|
52
|
+
await this.createCollectionConfig();
|
53
|
+
break;
|
54
|
+
case CHOICES.SETUP_DIRS_FILES:
|
55
|
+
await setupDirsFiles(false);
|
56
|
+
break;
|
57
|
+
case CHOICES.SETUP_DIRS_FILES_WITH_EXAMPLE_DATA:
|
58
|
+
await setupDirsFiles(true);
|
59
|
+
break;
|
60
|
+
case CHOICES.SYNCHRONIZE_CONFIGURATIONS:
|
61
|
+
await this.synchronizeConfigurations();
|
62
|
+
break;
|
63
|
+
case CHOICES.SYNC_DB:
|
64
|
+
await this.syncDb();
|
65
|
+
break;
|
66
|
+
case CHOICES.TRANSFER_DATA:
|
67
|
+
await this.transferData();
|
68
|
+
break;
|
69
|
+
case CHOICES.BACKUP_DATABASE:
|
70
|
+
await this.backupDatabase();
|
71
|
+
break;
|
72
|
+
case CHOICES.WIPE_DATABASE:
|
73
|
+
await this.wipeDatabase();
|
74
|
+
break;
|
75
|
+
case CHOICES.GENERATE_SCHEMAS:
|
76
|
+
await this.generateSchemas();
|
77
|
+
break;
|
78
|
+
case CHOICES.IMPORT_DATA:
|
79
|
+
await this.importData();
|
80
|
+
break;
|
81
|
+
case CHOICES.EXIT:
|
82
|
+
console.log("Exiting...");
|
83
|
+
return;
|
84
|
+
}
|
85
|
+
}
|
86
|
+
}
|
87
|
+
async selectDatabases(databases, message, multiSelect = true) {
|
88
|
+
const choices = databases.map((db) => ({ name: db.name, value: db }));
|
89
|
+
if (multiSelect) {
|
90
|
+
choices.unshift({ name: "Select All", value: "ALL" });
|
91
|
+
choices.push({ name: "Clear Selection", value: "CLEAR" });
|
92
|
+
}
|
93
|
+
const { selectedDatabases } = await inquirer.prompt([
|
94
|
+
{
|
95
|
+
type: multiSelect ? "checkbox" : "list",
|
96
|
+
name: "selectedDatabases",
|
97
|
+
message,
|
98
|
+
choices,
|
99
|
+
loop: false,
|
100
|
+
pageSize: 10,
|
101
|
+
},
|
102
|
+
]);
|
103
|
+
if (multiSelect) {
|
104
|
+
if (selectedDatabases.includes("ALL")) {
|
105
|
+
return databases;
|
106
|
+
}
|
107
|
+
else if (selectedDatabases.includes("CLEAR")) {
|
108
|
+
return [];
|
109
|
+
}
|
110
|
+
}
|
111
|
+
return selectedDatabases.filter((db) => typeof db !== "string");
|
112
|
+
}
|
113
|
+
async selectCollections(database, databasesClient, message, multiSelect = true) {
|
114
|
+
const collections = await fetchAllCollections(database.$id, databasesClient);
|
115
|
+
const choices = collections.map((collection) => ({
|
116
|
+
name: collection.name,
|
117
|
+
value: collection,
|
118
|
+
}));
|
119
|
+
if (multiSelect) {
|
120
|
+
choices.unshift({ name: "Select All", value: "ALL" });
|
121
|
+
choices.push({ name: "Clear Selection", value: "CLEAR" });
|
122
|
+
}
|
123
|
+
const { selectedCollections } = await inquirer.prompt([
|
124
|
+
{
|
125
|
+
type: multiSelect ? "checkbox" : "list",
|
126
|
+
name: "selectedCollections",
|
127
|
+
message,
|
128
|
+
choices,
|
129
|
+
loop: false,
|
130
|
+
pageSize: 10,
|
131
|
+
},
|
132
|
+
]);
|
133
|
+
if (multiSelect) {
|
134
|
+
if (selectedCollections.includes("ALL")) {
|
135
|
+
return collections;
|
136
|
+
}
|
137
|
+
else if (selectedCollections.includes("CLEAR")) {
|
138
|
+
return [];
|
139
|
+
}
|
140
|
+
}
|
141
|
+
return selectedCollections.filter((collection) => typeof collection !== "string");
|
142
|
+
}
|
143
|
+
async selectBuckets(buckets, message, multiSelect = true) {
|
144
|
+
const choices = buckets.map((bucket) => ({
|
145
|
+
name: bucket.name,
|
146
|
+
value: bucket.$id,
|
147
|
+
}));
|
148
|
+
if (multiSelect) {
|
149
|
+
choices.unshift({ name: "Select All", value: "ALL" });
|
150
|
+
choices.push({ name: "Clear Selection", value: "CLEAR" });
|
151
|
+
}
|
152
|
+
const { selectedBuckets } = await inquirer.prompt([
|
153
|
+
{
|
154
|
+
type: multiSelect ? "checkbox" : "list",
|
155
|
+
name: "selectedBuckets",
|
156
|
+
message,
|
157
|
+
choices,
|
158
|
+
loop: false,
|
159
|
+
pageSize: 10,
|
160
|
+
},
|
161
|
+
]);
|
162
|
+
if (multiSelect) {
|
163
|
+
if (selectedBuckets.includes("ALL")) {
|
164
|
+
return buckets;
|
165
|
+
}
|
166
|
+
else if (selectedBuckets.includes("CLEAR")) {
|
167
|
+
return [];
|
168
|
+
}
|
169
|
+
}
|
170
|
+
return selectedBuckets.map((id) => buckets.find((bucket) => bucket.$id === id));
|
171
|
+
}
|
172
|
+
async createCollectionConfig() {
|
173
|
+
const { collectionName } = await inquirer.prompt([
|
174
|
+
{
|
175
|
+
type: "input",
|
176
|
+
name: "collectionName",
|
177
|
+
message: "Enter the name of the collection:",
|
178
|
+
validate: (input) => input.trim() !== "" || "Collection name cannot be empty.",
|
179
|
+
},
|
180
|
+
]);
|
181
|
+
console.log(`Creating collection config file for '${collectionName}'...`);
|
182
|
+
createEmptyCollection(collectionName);
|
183
|
+
}
|
184
|
+
async configureBuckets(config, databases) {
|
185
|
+
const { storage } = this.controller;
|
186
|
+
if (!storage) {
|
187
|
+
throw new Error("Storage is not initialized. Is the config file correct and created?");
|
188
|
+
}
|
189
|
+
const allBuckets = await listBuckets(storage);
|
190
|
+
// If there are no buckets, ask to create one for each database
|
191
|
+
if (allBuckets.total === 0) {
|
192
|
+
for (const database of databases ?? config.databases) {
|
193
|
+
const { wantCreateBucket } = await inquirer.prompt([
|
194
|
+
{
|
195
|
+
type: "confirm",
|
196
|
+
name: "wantCreateBucket",
|
197
|
+
message: `There are no buckets. Do you want to create a bucket for the database "${database.name}"?`,
|
198
|
+
default: true,
|
199
|
+
},
|
200
|
+
]);
|
201
|
+
if (wantCreateBucket) {
|
202
|
+
const createdBucket = await this.createNewBucket(storage, database.name);
|
203
|
+
database.bucket = {
|
204
|
+
...createdBucket,
|
205
|
+
compression: createdBucket.compression,
|
206
|
+
};
|
207
|
+
}
|
208
|
+
}
|
209
|
+
return config;
|
210
|
+
}
|
211
|
+
// Configure global buckets
|
212
|
+
let globalBuckets = [];
|
213
|
+
if (allBuckets.total > 0) {
|
214
|
+
globalBuckets = await this.selectBuckets(allBuckets.buckets, "Select global buckets (buckets that are not associated with any specific database):", true);
|
215
|
+
config.buckets = globalBuckets.map((bucket) => ({
|
216
|
+
$id: bucket.$id,
|
217
|
+
name: bucket.name,
|
218
|
+
enabled: bucket.enabled,
|
219
|
+
maximumFileSize: bucket.maximumFileSize,
|
220
|
+
allowedFileExtensions: bucket.allowedFileExtensions,
|
221
|
+
compression: bucket.compression,
|
222
|
+
encryption: bucket.encryption,
|
223
|
+
antivirus: bucket.antivirus,
|
224
|
+
}));
|
225
|
+
}
|
226
|
+
else {
|
227
|
+
config.buckets = [];
|
228
|
+
}
|
229
|
+
// Configure database-specific buckets
|
230
|
+
for (const database of config.databases) {
|
231
|
+
const { assignBucket } = await inquirer.prompt([
|
232
|
+
{
|
233
|
+
type: "confirm",
|
234
|
+
name: "assignBucket",
|
235
|
+
message: `Do you want to assign or create a bucket for the database "${database.name}"?`,
|
236
|
+
default: false,
|
237
|
+
},
|
238
|
+
]);
|
239
|
+
if (assignBucket) {
|
240
|
+
const { action } = await inquirer.prompt([
|
241
|
+
{
|
242
|
+
type: "list",
|
243
|
+
name: "action",
|
244
|
+
message: `Choose an action for the database "${database.name}":`,
|
245
|
+
choices: [
|
246
|
+
{ name: "Assign existing bucket", value: "assign" },
|
247
|
+
{ name: "Create new bucket", value: "create" },
|
248
|
+
],
|
249
|
+
},
|
250
|
+
]);
|
251
|
+
if (action === "assign") {
|
252
|
+
const [selectedBucket] = await this.selectBuckets(allBuckets.buckets.filter((b) => !globalBuckets.some((gb) => gb.$id === b.$id)), `Select a bucket for the database "${database.name}":`, false);
|
253
|
+
if (selectedBucket) {
|
254
|
+
database.bucket = {
|
255
|
+
$id: selectedBucket.$id,
|
256
|
+
name: selectedBucket.name,
|
257
|
+
enabled: selectedBucket.enabled,
|
258
|
+
maximumFileSize: selectedBucket.maximumFileSize,
|
259
|
+
allowedFileExtensions: selectedBucket.allowedFileExtensions,
|
260
|
+
compression: selectedBucket.compression,
|
261
|
+
encryption: selectedBucket.encryption,
|
262
|
+
antivirus: selectedBucket.antivirus,
|
263
|
+
};
|
264
|
+
}
|
265
|
+
}
|
266
|
+
else if (action === "create") {
|
267
|
+
const createdBucket = await this.createNewBucket(storage, database.name);
|
268
|
+
database.bucket = {
|
269
|
+
...createdBucket,
|
270
|
+
compression: createdBucket.compression,
|
271
|
+
};
|
272
|
+
}
|
273
|
+
}
|
274
|
+
}
|
275
|
+
return config;
|
276
|
+
}
|
277
|
+
async createNewBucket(storage, databaseName) {
|
278
|
+
const { bucketName, bucketEnabled, bucketMaximumFileSize, bucketAllowedFileExtensions, bucketFileSecurity, bucketCompression, bucketCompressionType, bucketEncryption, bucketAntivirus, bucketId, } = await inquirer.prompt([
|
279
|
+
{
|
280
|
+
type: "input",
|
281
|
+
name: "bucketName",
|
282
|
+
message: `Enter the name of the bucket for database "${databaseName}":`,
|
283
|
+
default: `${databaseName}-bucket`,
|
284
|
+
},
|
285
|
+
{
|
286
|
+
type: "confirm",
|
287
|
+
name: "bucketEnabled",
|
288
|
+
message: "Is the bucket enabled?",
|
289
|
+
default: true,
|
290
|
+
},
|
291
|
+
{
|
292
|
+
type: "confirm",
|
293
|
+
name: "bucketFileSecurity",
|
294
|
+
message: "Do you want to enable file security for the bucket?",
|
295
|
+
default: false,
|
296
|
+
},
|
297
|
+
{
|
298
|
+
type: "number",
|
299
|
+
name: "bucketMaximumFileSize",
|
300
|
+
message: "Enter the maximum file size for the bucket (MB):",
|
301
|
+
default: 1000000,
|
302
|
+
},
|
303
|
+
{
|
304
|
+
type: "input",
|
305
|
+
name: "bucketAllowedFileExtensions",
|
306
|
+
message: "Enter the allowed file extensions for the bucket (comma separated):",
|
307
|
+
default: "",
|
308
|
+
},
|
309
|
+
{
|
310
|
+
type: "confirm",
|
311
|
+
name: "bucketCompression",
|
312
|
+
message: "Do you want to enable compression for the bucket?",
|
313
|
+
default: false,
|
314
|
+
},
|
315
|
+
{
|
316
|
+
type: "list",
|
317
|
+
name: "bucketCompressionType",
|
318
|
+
message: "Select the compression type for the bucket:",
|
319
|
+
choices: Object.values(Compression),
|
320
|
+
default: Compression.None,
|
321
|
+
when: (answers) => answers.bucketCompression,
|
322
|
+
},
|
323
|
+
{
|
324
|
+
type: "confirm",
|
325
|
+
name: "bucketEncryption",
|
326
|
+
message: "Do you want to enable encryption for the bucket?",
|
327
|
+
default: false,
|
328
|
+
},
|
329
|
+
{
|
330
|
+
type: "confirm",
|
331
|
+
name: "bucketAntivirus",
|
332
|
+
message: "Do you want to enable antivirus for the bucket?",
|
333
|
+
default: false,
|
334
|
+
},
|
335
|
+
{
|
336
|
+
type: "input",
|
337
|
+
name: "bucketId",
|
338
|
+
message: "Enter the ID of the bucket (or empty for auto-generation):",
|
339
|
+
},
|
340
|
+
]);
|
341
|
+
return await createBucket(storage, {
|
342
|
+
name: bucketName,
|
343
|
+
$permissions: [],
|
344
|
+
enabled: bucketEnabled,
|
345
|
+
fileSecurity: bucketFileSecurity,
|
346
|
+
maximumFileSize: bucketMaximumFileSize * 1024 * 1024,
|
347
|
+
allowedFileExtensions: bucketAllowedFileExtensions.length > 0
|
348
|
+
? bucketAllowedFileExtensions?.split(",")
|
349
|
+
: [],
|
350
|
+
compression: bucketCompressionType,
|
351
|
+
encryption: bucketEncryption,
|
352
|
+
antivirus: bucketAntivirus,
|
353
|
+
}, bucketId.length > 0 ? bucketId : ulid());
|
354
|
+
}
|
355
|
+
async syncDb() {
|
356
|
+
await this.controller.syncDb();
|
357
|
+
}
|
358
|
+
async synchronizeConfigurations() {
|
359
|
+
if (!this.controller.database) {
|
360
|
+
throw new Error("Database is not initialized. Is the config file correct and created?");
|
361
|
+
}
|
362
|
+
const databases = await fetchAllDatabases(this.controller.database);
|
363
|
+
const selectedDatabases = await this.selectDatabases(databases, "Select databases to synchronize (or select none to synchronize all):");
|
364
|
+
console.log("Configuring storage buckets...");
|
365
|
+
const updatedConfig = await this.configureBuckets(this.controller.config, selectedDatabases);
|
366
|
+
console.log("Synchronizing configurations...");
|
367
|
+
await this.controller.synchronizeConfigurations(selectedDatabases, updatedConfig);
|
368
|
+
}
|
369
|
+
async backupDatabase() {
|
370
|
+
if (!this.controller.database) {
|
371
|
+
throw new Error("Database is not initialized, is the config file correct & created?");
|
372
|
+
}
|
373
|
+
const databases = await fetchAllDatabases(this.controller.database);
|
374
|
+
const selectedDatabases = await this.selectDatabases(databases, "Select databases to backup (or select none to backup all):");
|
375
|
+
for (const db of selectedDatabases) {
|
376
|
+
console.log(`Backing up database: ${db.name}`);
|
377
|
+
await this.controller.backupDatabase(db);
|
378
|
+
}
|
379
|
+
}
|
380
|
+
async wipeDatabase() {
|
381
|
+
if (!this.controller.database || !this.controller.storage) {
|
382
|
+
throw new Error("Database or Storage is not initialized, is the config file correct & created?");
|
383
|
+
}
|
384
|
+
const databases = await fetchAllDatabases(this.controller.database);
|
385
|
+
const storage = await listBuckets(this.controller.storage);
|
386
|
+
const selectedDatabases = await this.selectDatabases(databases, "Select databases to wipe (or select none to skip database wipe):");
|
387
|
+
const { selectedStorage } = await inquirer.prompt([
|
388
|
+
{
|
389
|
+
type: "checkbox",
|
390
|
+
name: "selectedStorage",
|
391
|
+
message: "Select storage buckets to wipe (or select none to skip storage wipe):",
|
392
|
+
choices: storage.buckets.map((s) => ({ name: s.name, value: s.$id })),
|
393
|
+
},
|
394
|
+
]);
|
395
|
+
const { wipeUsers } = await inquirer.prompt([
|
396
|
+
{
|
397
|
+
type: "confirm",
|
398
|
+
name: "wipeUsers",
|
399
|
+
message: "Do you want to wipe users as well?",
|
400
|
+
default: false,
|
401
|
+
},
|
402
|
+
]);
|
403
|
+
const { confirm } = await inquirer.prompt([
|
404
|
+
{
|
405
|
+
type: "confirm",
|
406
|
+
name: "confirm",
|
407
|
+
message: "Are you sure you want to wipe the selected items? This action cannot be undone.",
|
408
|
+
default: false,
|
409
|
+
},
|
410
|
+
]);
|
411
|
+
if (confirm) {
|
412
|
+
console.log("Wiping selected items...");
|
413
|
+
for (const db of selectedDatabases) {
|
414
|
+
await this.controller.wipeDatabase(db);
|
415
|
+
}
|
416
|
+
for (const bucketId of selectedStorage) {
|
417
|
+
await this.controller.wipeDocumentStorage(bucketId);
|
418
|
+
}
|
419
|
+
if (wipeUsers) {
|
420
|
+
await this.controller.wipeUsers();
|
421
|
+
}
|
422
|
+
}
|
423
|
+
else {
|
424
|
+
console.log("Wipe operation cancelled.");
|
425
|
+
}
|
426
|
+
}
|
427
|
+
async generateSchemas() {
|
428
|
+
console.log("Generating schemas...");
|
429
|
+
await this.controller.generateSchemas();
|
430
|
+
}
|
431
|
+
async importData() {
|
432
|
+
if (!this.controller.database) {
|
433
|
+
throw new Error("Database is not initialized, is the config file correct & created?");
|
434
|
+
}
|
435
|
+
const databases = await fetchAllDatabases(this.controller.database);
|
436
|
+
const selectedDatabases = await this.selectDatabases(databases, "Select the database(s) to import data into:");
|
437
|
+
let selectedCollections = [];
|
438
|
+
for (const db of selectedDatabases) {
|
439
|
+
const dbCollections = await this.selectCollections(db, this.controller.database, `Select collections to import data into for database ${db.name} (or select none to import into all):`, true);
|
440
|
+
selectedCollections = [...selectedCollections, ...dbCollections];
|
441
|
+
}
|
442
|
+
const { shouldWriteFile } = await inquirer.prompt([
|
443
|
+
{
|
444
|
+
type: "confirm",
|
445
|
+
name: "shouldWriteFile",
|
446
|
+
message: "Do you want to write the imported data to a file?",
|
447
|
+
default: false,
|
448
|
+
},
|
449
|
+
]);
|
450
|
+
const { checkDuplicates } = await inquirer.prompt([
|
451
|
+
{
|
452
|
+
type: "confirm",
|
453
|
+
name: "checkDuplicates",
|
454
|
+
message: "Do you want to check for duplicates during import?",
|
455
|
+
default: true,
|
456
|
+
},
|
457
|
+
]);
|
458
|
+
console.log("Importing data...");
|
459
|
+
await this.controller.importData({
|
460
|
+
databases: selectedDatabases,
|
461
|
+
collections: selectedCollections.length > 0
|
462
|
+
? selectedCollections.map((c) => c.$id)
|
463
|
+
: undefined,
|
464
|
+
shouldWriteFile,
|
465
|
+
checkDuplicates,
|
466
|
+
});
|
467
|
+
}
|
468
|
+
async transferData() {
|
469
|
+
if (!this.controller.database) {
|
470
|
+
throw new Error("Database is not initialized, is the config file correct & created?");
|
471
|
+
}
|
472
|
+
const { isRemote } = await inquirer.prompt([
|
473
|
+
{
|
474
|
+
type: "confirm",
|
475
|
+
name: "isRemote",
|
476
|
+
message: "Is this a remote transfer?",
|
477
|
+
default: false,
|
478
|
+
},
|
479
|
+
]);
|
480
|
+
let sourceClient = this.controller.database;
|
481
|
+
let targetClient;
|
482
|
+
let sourceDatabases;
|
483
|
+
let targetDatabases;
|
484
|
+
let remoteOptions;
|
485
|
+
if (isRemote) {
|
486
|
+
remoteOptions = await inquirer.prompt([
|
487
|
+
{
|
488
|
+
type: "input",
|
489
|
+
name: "transferEndpoint",
|
490
|
+
message: "Enter the remote endpoint:",
|
491
|
+
},
|
492
|
+
{
|
493
|
+
type: "input",
|
494
|
+
name: "transferProject",
|
495
|
+
message: "Enter the remote project ID:",
|
496
|
+
},
|
497
|
+
{
|
498
|
+
type: "input",
|
499
|
+
name: "transferKey",
|
500
|
+
message: "Enter the remote API key:",
|
501
|
+
},
|
502
|
+
]);
|
503
|
+
const remoteClient = getClient(remoteOptions.transferEndpoint, remoteOptions.transferProject, remoteOptions.transferKey);
|
504
|
+
targetClient = new Databases(remoteClient);
|
505
|
+
sourceDatabases = await fetchAllDatabases(sourceClient);
|
506
|
+
targetDatabases = await fetchAllDatabases(targetClient);
|
507
|
+
}
|
508
|
+
else {
|
509
|
+
targetClient = sourceClient;
|
510
|
+
sourceDatabases = targetDatabases = await fetchAllDatabases(sourceClient);
|
511
|
+
}
|
512
|
+
const [fromDb] = await this.selectDatabases(sourceDatabases, "Select the source database:", false);
|
513
|
+
const [targetDb] = await this.selectDatabases(targetDatabases.filter((db) => db.$id !== fromDb.$id), "Select the target database:", false);
|
514
|
+
const selectedCollections = await this.selectCollections(fromDb, sourceClient, "Select collections to transfer (or select none to transfer all):");
|
515
|
+
const { transferStorage } = await inquirer.prompt([
|
516
|
+
{
|
517
|
+
type: "confirm",
|
518
|
+
name: "transferStorage",
|
519
|
+
message: "Do you want to transfer storage as well?",
|
520
|
+
default: false,
|
521
|
+
},
|
522
|
+
]);
|
523
|
+
let sourceBucket, targetBucket;
|
524
|
+
if (transferStorage) {
|
525
|
+
const sourceStorage = new Storage(this.controller.appwriteServer);
|
526
|
+
const targetStorage = isRemote
|
527
|
+
? new Storage(getClient(remoteOptions.transferEndpoint, remoteOptions.transferProject, remoteOptions.transferKey))
|
528
|
+
: sourceStorage;
|
529
|
+
const sourceBuckets = await listBuckets(sourceStorage);
|
530
|
+
const targetBuckets = isRemote
|
531
|
+
? await listBuckets(targetStorage)
|
532
|
+
: sourceBuckets;
|
533
|
+
[sourceBucket] = await this.selectBuckets(sourceBuckets.buckets, "Select the source bucket:", false);
|
534
|
+
[targetBucket] = await this.selectBuckets(targetBuckets.buckets, "Select the target bucket:", false);
|
535
|
+
}
|
536
|
+
let transferOptions = {
|
537
|
+
fromDb,
|
538
|
+
targetDb,
|
539
|
+
isRemote,
|
540
|
+
collections: selectedCollections.length > 0
|
541
|
+
? selectedCollections.map((c) => c.$id)
|
542
|
+
: undefined,
|
543
|
+
sourceBucket,
|
544
|
+
targetBucket,
|
545
|
+
};
|
546
|
+
if (isRemote && remoteOptions) {
|
547
|
+
transferOptions = {
|
548
|
+
...transferOptions,
|
549
|
+
...remoteOptions,
|
550
|
+
};
|
551
|
+
}
|
552
|
+
console.log("Transferring data...");
|
553
|
+
await this.controller.transferData(transferOptions);
|
554
|
+
}
|
555
|
+
}
|