appwrite-utils-cli 0.9.2 → 0.9.4

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.
@@ -1,608 +1,608 @@
1
- import { tryAwaitWithRetry } from "appwrite-utils";
2
- import {
3
- Client,
4
- Databases,
5
- IndexType,
6
- Query,
7
- Storage,
8
- type Models,
9
- } from "node-appwrite";
10
- import { InputFile } from "node-appwrite/file";
11
- import { getAppwriteClient } from "../utils/helperFunctions.js";
12
- import { createOrUpdateAttribute } from "./attributes.js";
13
- import { parseAttribute } from "appwrite-utils";
14
-
15
- export interface TransferOptions {
16
- fromDb: Models.Database;
17
- targetDb: Models.Database;
18
- isRemote: boolean;
19
- collections?: string[];
20
- transferEndpoint?: string;
21
- transferProject?: string;
22
- transferKey?: string;
23
- sourceBucket?: Models.Bucket;
24
- targetBucket?: Models.Bucket;
25
- }
26
-
27
- export const transferStorageLocalToLocal = async (
28
- storage: Storage,
29
- fromBucketId: string,
30
- toBucketId: string
31
- ) => {
32
- console.log(`Transferring files from ${fromBucketId} to ${toBucketId}`);
33
- let lastFileId: string | undefined;
34
- let fromFiles = await tryAwaitWithRetry(
35
- async () => await storage.listFiles(fromBucketId, [Query.limit(100)])
36
- );
37
- const allFromFiles = fromFiles.files;
38
- let numberOfFiles = 0;
39
-
40
- const downloadFileWithRetry = async (bucketId: string, fileId: string) => {
41
- let attempts = 3;
42
- while (attempts > 0) {
43
- try {
44
- return await storage.getFileDownload(bucketId, fileId);
45
- } catch (error) {
46
- console.error(`Error downloading file ${fileId}: ${error}`);
47
- attempts--;
48
- if (attempts === 0) throw error;
49
- }
50
- }
51
- };
52
-
53
- if (fromFiles.files.length < 100) {
54
- for (const file of allFromFiles) {
55
- const fileData = await tryAwaitWithRetry(
56
- async () => await downloadFileWithRetry(file.bucketId, file.$id)
57
- );
58
- if (!fileData) {
59
- console.error(`Error downloading file ${file.$id}`);
60
- continue;
61
- }
62
- const fileToCreate = InputFile.fromBuffer(
63
- new Uint8Array(fileData),
64
- file.name
65
- );
66
- console.log(`Creating file: ${file.name}`);
67
- tryAwaitWithRetry(
68
- async () =>
69
- await storage.createFile(
70
- toBucketId,
71
- file.$id,
72
- fileToCreate,
73
- file.$permissions
74
- )
75
- );
76
- numberOfFiles++;
77
- }
78
- } else {
79
- lastFileId = fromFiles.files[fromFiles.files.length - 1].$id;
80
- while (lastFileId) {
81
- const files = await tryAwaitWithRetry(
82
- async () =>
83
- await storage.listFiles(fromBucketId, [
84
- Query.limit(100),
85
- Query.cursorAfter(lastFileId!),
86
- ])
87
- );
88
- allFromFiles.push(...files.files);
89
- if (files.files.length < 100) {
90
- lastFileId = undefined;
91
- } else {
92
- lastFileId = files.files[files.files.length - 1].$id;
93
- }
94
- }
95
- for (const file of allFromFiles) {
96
- const fileData = await tryAwaitWithRetry(
97
- async () => await downloadFileWithRetry(file.bucketId, file.$id)
98
- );
99
- if (!fileData) {
100
- console.error(`Error downloading file ${file.$id}`);
101
- continue;
102
- }
103
- const fileToCreate = InputFile.fromBuffer(
104
- new Uint8Array(fileData),
105
- file.name
106
- );
107
- await tryAwaitWithRetry(
108
- async () =>
109
- await storage.createFile(
110
- toBucketId,
111
- file.$id,
112
- fileToCreate,
113
- file.$permissions
114
- )
115
- );
116
- numberOfFiles++;
117
- }
118
- }
119
-
120
- console.log(
121
- `Transferred ${numberOfFiles} files from ${fromBucketId} to ${toBucketId}`
122
- );
123
- };
124
-
125
- export const transferStorageLocalToRemote = async (
126
- localStorage: Storage,
127
- endpoint: string,
128
- projectId: string,
129
- apiKey: string,
130
- fromBucketId: string,
131
- toBucketId: string
132
- ) => {
133
- console.log(
134
- `Transferring files from current storage ${fromBucketId} to ${endpoint} bucket ${toBucketId}`
135
- );
136
- const client = getAppwriteClient(endpoint, apiKey, projectId);
137
- const remoteStorage = new Storage(client);
138
- let numberOfFiles = 0;
139
- let lastFileId: string | undefined;
140
- let fromFiles = await tryAwaitWithRetry(
141
- async () => await localStorage.listFiles(fromBucketId, [Query.limit(100)])
142
- );
143
- const allFromFiles = fromFiles.files;
144
- if (fromFiles.files.length === 100) {
145
- lastFileId = fromFiles.files[fromFiles.files.length - 1].$id;
146
- while (lastFileId) {
147
- const files = await tryAwaitWithRetry(
148
- async () =>
149
- await localStorage.listFiles(fromBucketId, [
150
- Query.limit(100),
151
- Query.cursorAfter(lastFileId!),
152
- ])
153
- );
154
- allFromFiles.push(...files.files);
155
- if (files.files.length < 100) {
156
- break;
157
- }
158
- lastFileId = files.files[files.files.length - 1].$id;
159
- }
160
- }
161
-
162
- for (const file of allFromFiles) {
163
- const fileData = await tryAwaitWithRetry(
164
- async () => await localStorage.getFileDownload(file.bucketId, file.$id)
165
- );
166
- const fileToCreate = InputFile.fromBuffer(
167
- new Uint8Array(fileData),
168
- file.name
169
- );
170
- await tryAwaitWithRetry(
171
- async () =>
172
- await remoteStorage.createFile(
173
- toBucketId,
174
- file.$id,
175
- fileToCreate,
176
- file.$permissions
177
- )
178
- );
179
- numberOfFiles++;
180
- }
181
- console.log(
182
- `Transferred ${numberOfFiles} files from ${fromBucketId} to ${toBucketId}`
183
- );
184
- };
185
-
186
- /**
187
- * Transfers all documents from one collection to another in a different database
188
- * within the same Appwrite Project
189
- */
190
- export const transferDocumentsBetweenDbsLocalToLocal = async (
191
- db: Databases,
192
- fromDbId: string,
193
- toDbId: string,
194
- fromCollId: string,
195
- toCollId: string
196
- ) => {
197
- let fromCollDocs = await tryAwaitWithRetry(async () =>
198
- db.listDocuments(fromDbId, fromCollId, [Query.limit(50)])
199
- );
200
- let totalDocumentsTransferred = 0;
201
-
202
- if (fromCollDocs.documents.length === 0) {
203
- console.log(`No documents found in collection ${fromCollId}`);
204
- return;
205
- } else if (fromCollDocs.documents.length < 50) {
206
- const batchedPromises = fromCollDocs.documents.map((doc) => {
207
- const toCreateObject: Partial<typeof doc> = {
208
- ...doc,
209
- };
210
- delete toCreateObject.$databaseId;
211
- delete toCreateObject.$collectionId;
212
- delete toCreateObject.$createdAt;
213
- delete toCreateObject.$updatedAt;
214
- delete toCreateObject.$id;
215
- delete toCreateObject.$permissions;
216
- return tryAwaitWithRetry(
217
- async () =>
218
- await db.createDocument(
219
- toDbId,
220
- toCollId,
221
- doc.$id,
222
- toCreateObject,
223
- doc.$permissions
224
- )
225
- );
226
- });
227
- await Promise.all(batchedPromises);
228
- totalDocumentsTransferred += fromCollDocs.documents.length;
229
- } else {
230
- const batchedPromises = fromCollDocs.documents.map((doc) => {
231
- const toCreateObject: Partial<typeof doc> = {
232
- ...doc,
233
- };
234
- delete toCreateObject.$databaseId;
235
- delete toCreateObject.$collectionId;
236
- delete toCreateObject.$createdAt;
237
- delete toCreateObject.$updatedAt;
238
- delete toCreateObject.$id;
239
- delete toCreateObject.$permissions;
240
- return tryAwaitWithRetry(async () =>
241
- db.createDocument(
242
- toDbId,
243
- toCollId,
244
- doc.$id,
245
- toCreateObject,
246
- doc.$permissions
247
- )
248
- );
249
- });
250
- await Promise.all(batchedPromises);
251
- totalDocumentsTransferred += fromCollDocs.documents.length;
252
- while (fromCollDocs.documents.length === 50) {
253
- fromCollDocs = await tryAwaitWithRetry(
254
- async () =>
255
- await db.listDocuments(fromDbId, fromCollId, [
256
- Query.limit(50),
257
- Query.cursorAfter(
258
- fromCollDocs.documents[fromCollDocs.documents.length - 1].$id
259
- ),
260
- ])
261
- );
262
- const batchedPromises = fromCollDocs.documents.map((doc) => {
263
- const toCreateObject: Partial<typeof doc> = {
264
- ...doc,
265
- };
266
- delete toCreateObject.$databaseId;
267
- delete toCreateObject.$collectionId;
268
- delete toCreateObject.$createdAt;
269
- delete toCreateObject.$updatedAt;
270
- delete toCreateObject.$id;
271
- delete toCreateObject.$permissions;
272
- return tryAwaitWithRetry(
273
- async () =>
274
- await db.createDocument(
275
- toDbId,
276
- toCollId,
277
- doc.$id,
278
- toCreateObject,
279
- doc.$permissions
280
- )
281
- );
282
- });
283
- await Promise.all(batchedPromises);
284
- totalDocumentsTransferred += fromCollDocs.documents.length;
285
- }
286
- }
287
-
288
- console.log(
289
- `Transferred ${totalDocumentsTransferred} documents from database ${fromDbId} to database ${toDbId} -- collection ${fromCollId} to collection ${toCollId}`
290
- );
291
- };
292
-
293
- export const transferDocumentsBetweenDbsLocalToRemote = async (
294
- localDb: Databases,
295
- endpoint: string,
296
- projectId: string,
297
- apiKey: string,
298
- fromDbId: string,
299
- toDbId: string,
300
- fromCollId: string,
301
- toCollId: string
302
- ) => {
303
- const client = new Client()
304
- .setEndpoint(endpoint)
305
- .setProject(projectId)
306
- .setKey(apiKey);
307
- let totalDocumentsTransferred = 0;
308
- const remoteDb = new Databases(client);
309
- let fromCollDocs = await tryAwaitWithRetry(async () =>
310
- localDb.listDocuments(fromDbId, fromCollId, [Query.limit(50)])
311
- );
312
-
313
- if (fromCollDocs.documents.length === 0) {
314
- console.log(`No documents found in collection ${fromCollId}`);
315
- return;
316
- } else if (fromCollDocs.documents.length < 50) {
317
- const batchedPromises = fromCollDocs.documents.map((doc) => {
318
- const toCreateObject: Partial<typeof doc> = {
319
- ...doc,
320
- };
321
- delete toCreateObject.$databaseId;
322
- delete toCreateObject.$collectionId;
323
- delete toCreateObject.$createdAt;
324
- delete toCreateObject.$updatedAt;
325
- delete toCreateObject.$id;
326
- delete toCreateObject.$permissions;
327
- return tryAwaitWithRetry(async () =>
328
- remoteDb.createDocument(
329
- toDbId,
330
- toCollId,
331
- doc.$id,
332
- toCreateObject,
333
- doc.$permissions
334
- )
335
- );
336
- });
337
- await Promise.all(batchedPromises);
338
- totalDocumentsTransferred += fromCollDocs.documents.length;
339
- } else {
340
- const batchedPromises = fromCollDocs.documents.map((doc) => {
341
- const toCreateObject: Partial<typeof doc> = {
342
- ...doc,
343
- };
344
- delete toCreateObject.$databaseId;
345
- delete toCreateObject.$collectionId;
346
- delete toCreateObject.$createdAt;
347
- delete toCreateObject.$updatedAt;
348
- delete toCreateObject.$id;
349
- delete toCreateObject.$permissions;
350
- return tryAwaitWithRetry(async () =>
351
- remoteDb.createDocument(
352
- toDbId,
353
- toCollId,
354
- doc.$id,
355
- toCreateObject,
356
- doc.$permissions
357
- )
358
- );
359
- });
360
- await Promise.all(batchedPromises);
361
- totalDocumentsTransferred += fromCollDocs.documents.length;
362
- while (fromCollDocs.documents.length === 50) {
363
- fromCollDocs = await tryAwaitWithRetry(async () =>
364
- localDb.listDocuments(fromDbId, fromCollId, [
365
- Query.limit(50),
366
- Query.cursorAfter(
367
- fromCollDocs.documents[fromCollDocs.documents.length - 1].$id
368
- ),
369
- ])
370
- );
371
- const batchedPromises = fromCollDocs.documents.map((doc) => {
372
- const toCreateObject: Partial<typeof doc> = {
373
- ...doc,
374
- };
375
- delete toCreateObject.$databaseId;
376
- delete toCreateObject.$collectionId;
377
- delete toCreateObject.$createdAt;
378
- delete toCreateObject.$updatedAt;
379
- delete toCreateObject.$id;
380
- delete toCreateObject.$permissions;
381
- return tryAwaitWithRetry(async () =>
382
- remoteDb.createDocument(
383
- toDbId,
384
- toCollId,
385
- doc.$id,
386
- toCreateObject,
387
- doc.$permissions
388
- )
389
- );
390
- });
391
- await Promise.all(batchedPromises);
392
- totalDocumentsTransferred += fromCollDocs.documents.length;
393
- }
394
- }
395
- console.log(
396
- `Total documents transferred from database ${fromDbId} to database ${toDbId} -- collection ${fromCollId} to collection ${toCollId}: ${totalDocumentsTransferred}`
397
- );
398
- };
399
-
400
- /**
401
- * Transfers all collections and documents from one local database to another local database.
402
- *
403
- * @param {Databases} localDb - The local database instance.
404
- * @param {string} fromDbId - The ID of the source database.
405
- * @param {string} targetDbId - The ID of the target database.
406
- * @return {Promise<void>} A promise that resolves when the transfer is complete.
407
- */
408
- export const transferDatabaseLocalToLocal = async (
409
- localDb: Databases,
410
- fromDbId: string,
411
- targetDbId: string
412
- ) => {
413
- let lastCollectionId: string | undefined;
414
- let fromCollections = await tryAwaitWithRetry(
415
- async () => await localDb.listCollections(fromDbId, [Query.limit(50)])
416
- );
417
- const allFromCollections = fromCollections.collections;
418
- if (fromCollections.collections.length < 50) {
419
- lastCollectionId = undefined;
420
- } else {
421
- lastCollectionId =
422
- fromCollections.collections[fromCollections.collections.length - 1].$id;
423
- while (lastCollectionId) {
424
- const collections = await localDb.listCollections(fromDbId, [
425
- Query.limit(50),
426
- Query.cursorAfter(lastCollectionId),
427
- ]);
428
- allFromCollections.push(...collections.collections);
429
- if (collections.collections.length < 50) {
430
- break;
431
- }
432
- lastCollectionId =
433
- collections.collections[collections.collections.length - 1].$id;
434
- }
435
- }
436
- lastCollectionId = undefined;
437
- let toCollections = await tryAwaitWithRetry(
438
- async () => await localDb.listCollections(targetDbId, [Query.limit(50)])
439
- );
440
- const allToCollections = toCollections.collections;
441
- if (toCollections.collections.length < 50) {
442
- } else {
443
- lastCollectionId =
444
- toCollections.collections[toCollections.collections.length - 1].$id;
445
- while (lastCollectionId) {
446
- const collections = await localDb.listCollections(targetDbId, [
447
- Query.limit(50),
448
- Query.cursorAfter(lastCollectionId),
449
- ]);
450
- allToCollections.push(...collections.collections);
451
- if (collections.collections.length < 50) {
452
- lastCollectionId = undefined;
453
- } else {
454
- lastCollectionId =
455
- collections.collections[collections.collections.length - 1].$id;
456
- }
457
- }
458
- }
459
- for (const collection of allFromCollections) {
460
- const toCollection = allToCollections.find((c) => c.$id === collection.$id);
461
- if (toCollection) {
462
- await transferDocumentsBetweenDbsLocalToLocal(
463
- localDb,
464
- fromDbId,
465
- targetDbId,
466
- collection.$id,
467
- toCollection.$id
468
- );
469
- } else {
470
- console.log(
471
- `Collection ${collection.name} not found in destination database, creating...`
472
- );
473
- const newCollection = await tryAwaitWithRetry(
474
- async () =>
475
- await localDb.createCollection(
476
- targetDbId,
477
- collection.$id,
478
- collection.name,
479
- collection.$permissions,
480
- collection.documentSecurity,
481
- collection.enabled
482
- )
483
- );
484
- console.log(`Collection ${newCollection.name} created`);
485
- for (const attribute of collection.attributes) {
486
- await tryAwaitWithRetry(
487
- async () =>
488
- await createOrUpdateAttribute(
489
- localDb,
490
- targetDbId,
491
- newCollection,
492
- parseAttribute(attribute as any)
493
- )
494
- );
495
- }
496
- for (const index of collection.indexes) {
497
- await tryAwaitWithRetry(
498
- async () =>
499
- await localDb.createIndex(
500
- targetDbId,
501
- newCollection.$id,
502
- index.key,
503
- index.type as IndexType,
504
- index.attributes,
505
- index.orders
506
- )
507
- );
508
- }
509
- await transferDocumentsBetweenDbsLocalToLocal(
510
- localDb,
511
- fromDbId,
512
- targetDbId,
513
- collection.$id,
514
- newCollection.$id
515
- );
516
- }
517
- }
518
- };
519
-
520
- export const transferDatabaseLocalToRemote = async (
521
- localDb: Databases,
522
- endpoint: string,
523
- projectId: string,
524
- apiKey: string,
525
- fromDbId: string,
526
- toDbId: string
527
- ) => {
528
- const client = getAppwriteClient(endpoint, projectId, apiKey);
529
- const remoteDb = new Databases(client);
530
-
531
- let lastCollectionId: string | undefined;
532
- let fromCollections = await tryAwaitWithRetry(
533
- async () => await localDb.listCollections(fromDbId, [Query.limit(50)])
534
- );
535
- const allFromCollections = fromCollections.collections;
536
- if (fromCollections.collections.length < 50) {
537
- } else {
538
- lastCollectionId =
539
- fromCollections.collections[fromCollections.collections.length - 1].$id;
540
- while (lastCollectionId) {
541
- const collections = await tryAwaitWithRetry(
542
- async () =>
543
- await localDb.listCollections(fromDbId, [
544
- Query.limit(50),
545
- Query.cursorAfter(lastCollectionId!),
546
- ])
547
- );
548
- allFromCollections.push(...collections.collections);
549
- if (collections.collections.length < 50) {
550
- break;
551
- }
552
- lastCollectionId =
553
- collections.collections[collections.collections.length - 1].$id;
554
- }
555
- }
556
-
557
- for (const collection of allFromCollections) {
558
- const toCollection = await tryAwaitWithRetry(
559
- async () =>
560
- await remoteDb.createCollection(
561
- toDbId,
562
- collection.$id,
563
- collection.name,
564
- collection.$permissions,
565
- collection.documentSecurity,
566
- collection.enabled
567
- )
568
- );
569
- console.log(`Collection ${toCollection.name} created`);
570
-
571
- for (const attribute of collection.attributes) {
572
- await tryAwaitWithRetry(
573
- async () =>
574
- await createOrUpdateAttribute(
575
- remoteDb,
576
- toDbId,
577
- toCollection,
578
- parseAttribute(attribute as any)
579
- )
580
- );
581
- }
582
-
583
- for (const index of collection.indexes) {
584
- await tryAwaitWithRetry(
585
- async () =>
586
- await remoteDb.createIndex(
587
- toDbId,
588
- toCollection.$id,
589
- index.key,
590
- index.type as IndexType,
591
- index.attributes,
592
- index.orders
593
- )
594
- );
595
- }
596
-
597
- await transferDocumentsBetweenDbsLocalToRemote(
598
- localDb,
599
- endpoint,
600
- projectId,
601
- apiKey,
602
- fromDbId,
603
- toDbId,
604
- collection.$id,
605
- toCollection.$id
606
- );
607
- }
608
- };
1
+ import { tryAwaitWithRetry } from "appwrite-utils";
2
+ import {
3
+ Client,
4
+ Databases,
5
+ IndexType,
6
+ Query,
7
+ Storage,
8
+ type Models,
9
+ } from "node-appwrite";
10
+ import { InputFile } from "node-appwrite/file";
11
+ import { getAppwriteClient } from "../utils/helperFunctions.js";
12
+ import { createOrUpdateAttribute } from "./attributes.js";
13
+ import { parseAttribute } from "appwrite-utils";
14
+
15
+ export interface TransferOptions {
16
+ fromDb: Models.Database;
17
+ targetDb: Models.Database;
18
+ isRemote: boolean;
19
+ collections?: string[];
20
+ transferEndpoint?: string;
21
+ transferProject?: string;
22
+ transferKey?: string;
23
+ sourceBucket?: Models.Bucket;
24
+ targetBucket?: Models.Bucket;
25
+ }
26
+
27
+ export const transferStorageLocalToLocal = async (
28
+ storage: Storage,
29
+ fromBucketId: string,
30
+ toBucketId: string
31
+ ) => {
32
+ console.log(`Transferring files from ${fromBucketId} to ${toBucketId}`);
33
+ let lastFileId: string | undefined;
34
+ let fromFiles = await tryAwaitWithRetry(
35
+ async () => await storage.listFiles(fromBucketId, [Query.limit(100)])
36
+ );
37
+ const allFromFiles = fromFiles.files;
38
+ let numberOfFiles = 0;
39
+
40
+ const downloadFileWithRetry = async (bucketId: string, fileId: string) => {
41
+ let attempts = 3;
42
+ while (attempts > 0) {
43
+ try {
44
+ return await storage.getFileDownload(bucketId, fileId);
45
+ } catch (error) {
46
+ console.error(`Error downloading file ${fileId}: ${error}`);
47
+ attempts--;
48
+ if (attempts === 0) throw error;
49
+ }
50
+ }
51
+ };
52
+
53
+ if (fromFiles.files.length < 100) {
54
+ for (const file of allFromFiles) {
55
+ const fileData = await tryAwaitWithRetry(
56
+ async () => await downloadFileWithRetry(file.bucketId, file.$id)
57
+ );
58
+ if (!fileData) {
59
+ console.error(`Error downloading file ${file.$id}`);
60
+ continue;
61
+ }
62
+ const fileToCreate = InputFile.fromBuffer(
63
+ new Uint8Array(fileData),
64
+ file.name
65
+ );
66
+ console.log(`Creating file: ${file.name}`);
67
+ tryAwaitWithRetry(
68
+ async () =>
69
+ await storage.createFile(
70
+ toBucketId,
71
+ file.$id,
72
+ fileToCreate,
73
+ file.$permissions
74
+ )
75
+ );
76
+ numberOfFiles++;
77
+ }
78
+ } else {
79
+ lastFileId = fromFiles.files[fromFiles.files.length - 1].$id;
80
+ while (lastFileId) {
81
+ const files = await tryAwaitWithRetry(
82
+ async () =>
83
+ await storage.listFiles(fromBucketId, [
84
+ Query.limit(100),
85
+ Query.cursorAfter(lastFileId!),
86
+ ])
87
+ );
88
+ allFromFiles.push(...files.files);
89
+ if (files.files.length < 100) {
90
+ lastFileId = undefined;
91
+ } else {
92
+ lastFileId = files.files[files.files.length - 1].$id;
93
+ }
94
+ }
95
+ for (const file of allFromFiles) {
96
+ const fileData = await tryAwaitWithRetry(
97
+ async () => await downloadFileWithRetry(file.bucketId, file.$id)
98
+ );
99
+ if (!fileData) {
100
+ console.error(`Error downloading file ${file.$id}`);
101
+ continue;
102
+ }
103
+ const fileToCreate = InputFile.fromBuffer(
104
+ new Uint8Array(fileData),
105
+ file.name
106
+ );
107
+ await tryAwaitWithRetry(
108
+ async () =>
109
+ await storage.createFile(
110
+ toBucketId,
111
+ file.$id,
112
+ fileToCreate,
113
+ file.$permissions
114
+ )
115
+ );
116
+ numberOfFiles++;
117
+ }
118
+ }
119
+
120
+ console.log(
121
+ `Transferred ${numberOfFiles} files from ${fromBucketId} to ${toBucketId}`
122
+ );
123
+ };
124
+
125
+ export const transferStorageLocalToRemote = async (
126
+ localStorage: Storage,
127
+ endpoint: string,
128
+ projectId: string,
129
+ apiKey: string,
130
+ fromBucketId: string,
131
+ toBucketId: string
132
+ ) => {
133
+ console.log(
134
+ `Transferring files from current storage ${fromBucketId} to ${endpoint} bucket ${toBucketId}`
135
+ );
136
+ const client = getAppwriteClient(endpoint, apiKey, projectId);
137
+ const remoteStorage = new Storage(client);
138
+ let numberOfFiles = 0;
139
+ let lastFileId: string | undefined;
140
+ let fromFiles = await tryAwaitWithRetry(
141
+ async () => await localStorage.listFiles(fromBucketId, [Query.limit(100)])
142
+ );
143
+ const allFromFiles = fromFiles.files;
144
+ if (fromFiles.files.length === 100) {
145
+ lastFileId = fromFiles.files[fromFiles.files.length - 1].$id;
146
+ while (lastFileId) {
147
+ const files = await tryAwaitWithRetry(
148
+ async () =>
149
+ await localStorage.listFiles(fromBucketId, [
150
+ Query.limit(100),
151
+ Query.cursorAfter(lastFileId!),
152
+ ])
153
+ );
154
+ allFromFiles.push(...files.files);
155
+ if (files.files.length < 100) {
156
+ break;
157
+ }
158
+ lastFileId = files.files[files.files.length - 1].$id;
159
+ }
160
+ }
161
+
162
+ for (const file of allFromFiles) {
163
+ const fileData = await tryAwaitWithRetry(
164
+ async () => await localStorage.getFileDownload(file.bucketId, file.$id)
165
+ );
166
+ const fileToCreate = InputFile.fromBuffer(
167
+ new Uint8Array(fileData),
168
+ file.name
169
+ );
170
+ await tryAwaitWithRetry(
171
+ async () =>
172
+ await remoteStorage.createFile(
173
+ toBucketId,
174
+ file.$id,
175
+ fileToCreate,
176
+ file.$permissions
177
+ )
178
+ );
179
+ numberOfFiles++;
180
+ }
181
+ console.log(
182
+ `Transferred ${numberOfFiles} files from ${fromBucketId} to ${toBucketId}`
183
+ );
184
+ };
185
+
186
+ /**
187
+ * Transfers all documents from one collection to another in a different database
188
+ * within the same Appwrite Project
189
+ */
190
+ export const transferDocumentsBetweenDbsLocalToLocal = async (
191
+ db: Databases,
192
+ fromDbId: string,
193
+ toDbId: string,
194
+ fromCollId: string,
195
+ toCollId: string
196
+ ) => {
197
+ let fromCollDocs = await tryAwaitWithRetry(async () =>
198
+ db.listDocuments(fromDbId, fromCollId, [Query.limit(50)])
199
+ );
200
+ let totalDocumentsTransferred = 0;
201
+
202
+ if (fromCollDocs.documents.length === 0) {
203
+ console.log(`No documents found in collection ${fromCollId}`);
204
+ return;
205
+ } else if (fromCollDocs.documents.length < 50) {
206
+ const batchedPromises = fromCollDocs.documents.map((doc) => {
207
+ const toCreateObject: Partial<typeof doc> = {
208
+ ...doc,
209
+ };
210
+ delete toCreateObject.$databaseId;
211
+ delete toCreateObject.$collectionId;
212
+ delete toCreateObject.$createdAt;
213
+ delete toCreateObject.$updatedAt;
214
+ delete toCreateObject.$id;
215
+ delete toCreateObject.$permissions;
216
+ return tryAwaitWithRetry(
217
+ async () =>
218
+ await db.createDocument(
219
+ toDbId,
220
+ toCollId,
221
+ doc.$id,
222
+ toCreateObject,
223
+ doc.$permissions
224
+ )
225
+ );
226
+ });
227
+ await Promise.all(batchedPromises);
228
+ totalDocumentsTransferred += fromCollDocs.documents.length;
229
+ } else {
230
+ const batchedPromises = fromCollDocs.documents.map((doc) => {
231
+ const toCreateObject: Partial<typeof doc> = {
232
+ ...doc,
233
+ };
234
+ delete toCreateObject.$databaseId;
235
+ delete toCreateObject.$collectionId;
236
+ delete toCreateObject.$createdAt;
237
+ delete toCreateObject.$updatedAt;
238
+ delete toCreateObject.$id;
239
+ delete toCreateObject.$permissions;
240
+ return tryAwaitWithRetry(async () =>
241
+ db.createDocument(
242
+ toDbId,
243
+ toCollId,
244
+ doc.$id,
245
+ toCreateObject,
246
+ doc.$permissions
247
+ )
248
+ );
249
+ });
250
+ await Promise.all(batchedPromises);
251
+ totalDocumentsTransferred += fromCollDocs.documents.length;
252
+ while (fromCollDocs.documents.length === 50) {
253
+ fromCollDocs = await tryAwaitWithRetry(
254
+ async () =>
255
+ await db.listDocuments(fromDbId, fromCollId, [
256
+ Query.limit(50),
257
+ Query.cursorAfter(
258
+ fromCollDocs.documents[fromCollDocs.documents.length - 1].$id
259
+ ),
260
+ ])
261
+ );
262
+ const batchedPromises = fromCollDocs.documents.map((doc) => {
263
+ const toCreateObject: Partial<typeof doc> = {
264
+ ...doc,
265
+ };
266
+ delete toCreateObject.$databaseId;
267
+ delete toCreateObject.$collectionId;
268
+ delete toCreateObject.$createdAt;
269
+ delete toCreateObject.$updatedAt;
270
+ delete toCreateObject.$id;
271
+ delete toCreateObject.$permissions;
272
+ return tryAwaitWithRetry(
273
+ async () =>
274
+ await db.createDocument(
275
+ toDbId,
276
+ toCollId,
277
+ doc.$id,
278
+ toCreateObject,
279
+ doc.$permissions
280
+ )
281
+ );
282
+ });
283
+ await Promise.all(batchedPromises);
284
+ totalDocumentsTransferred += fromCollDocs.documents.length;
285
+ }
286
+ }
287
+
288
+ console.log(
289
+ `Transferred ${totalDocumentsTransferred} documents from database ${fromDbId} to database ${toDbId} -- collection ${fromCollId} to collection ${toCollId}`
290
+ );
291
+ };
292
+
293
+ export const transferDocumentsBetweenDbsLocalToRemote = async (
294
+ localDb: Databases,
295
+ endpoint: string,
296
+ projectId: string,
297
+ apiKey: string,
298
+ fromDbId: string,
299
+ toDbId: string,
300
+ fromCollId: string,
301
+ toCollId: string
302
+ ) => {
303
+ const client = new Client()
304
+ .setEndpoint(endpoint)
305
+ .setProject(projectId)
306
+ .setKey(apiKey);
307
+ let totalDocumentsTransferred = 0;
308
+ const remoteDb = new Databases(client);
309
+ let fromCollDocs = await tryAwaitWithRetry(async () =>
310
+ localDb.listDocuments(fromDbId, fromCollId, [Query.limit(50)])
311
+ );
312
+
313
+ if (fromCollDocs.documents.length === 0) {
314
+ console.log(`No documents found in collection ${fromCollId}`);
315
+ return;
316
+ } else if (fromCollDocs.documents.length < 50) {
317
+ const batchedPromises = fromCollDocs.documents.map((doc) => {
318
+ const toCreateObject: Partial<typeof doc> = {
319
+ ...doc,
320
+ };
321
+ delete toCreateObject.$databaseId;
322
+ delete toCreateObject.$collectionId;
323
+ delete toCreateObject.$createdAt;
324
+ delete toCreateObject.$updatedAt;
325
+ delete toCreateObject.$id;
326
+ delete toCreateObject.$permissions;
327
+ return tryAwaitWithRetry(async () =>
328
+ remoteDb.createDocument(
329
+ toDbId,
330
+ toCollId,
331
+ doc.$id,
332
+ toCreateObject,
333
+ doc.$permissions
334
+ )
335
+ );
336
+ });
337
+ await Promise.all(batchedPromises);
338
+ totalDocumentsTransferred += fromCollDocs.documents.length;
339
+ } else {
340
+ const batchedPromises = fromCollDocs.documents.map((doc) => {
341
+ const toCreateObject: Partial<typeof doc> = {
342
+ ...doc,
343
+ };
344
+ delete toCreateObject.$databaseId;
345
+ delete toCreateObject.$collectionId;
346
+ delete toCreateObject.$createdAt;
347
+ delete toCreateObject.$updatedAt;
348
+ delete toCreateObject.$id;
349
+ delete toCreateObject.$permissions;
350
+ return tryAwaitWithRetry(async () =>
351
+ remoteDb.createDocument(
352
+ toDbId,
353
+ toCollId,
354
+ doc.$id,
355
+ toCreateObject,
356
+ doc.$permissions
357
+ )
358
+ );
359
+ });
360
+ await Promise.all(batchedPromises);
361
+ totalDocumentsTransferred += fromCollDocs.documents.length;
362
+ while (fromCollDocs.documents.length === 50) {
363
+ fromCollDocs = await tryAwaitWithRetry(async () =>
364
+ localDb.listDocuments(fromDbId, fromCollId, [
365
+ Query.limit(50),
366
+ Query.cursorAfter(
367
+ fromCollDocs.documents[fromCollDocs.documents.length - 1].$id
368
+ ),
369
+ ])
370
+ );
371
+ const batchedPromises = fromCollDocs.documents.map((doc) => {
372
+ const toCreateObject: Partial<typeof doc> = {
373
+ ...doc,
374
+ };
375
+ delete toCreateObject.$databaseId;
376
+ delete toCreateObject.$collectionId;
377
+ delete toCreateObject.$createdAt;
378
+ delete toCreateObject.$updatedAt;
379
+ delete toCreateObject.$id;
380
+ delete toCreateObject.$permissions;
381
+ return tryAwaitWithRetry(async () =>
382
+ remoteDb.createDocument(
383
+ toDbId,
384
+ toCollId,
385
+ doc.$id,
386
+ toCreateObject,
387
+ doc.$permissions
388
+ )
389
+ );
390
+ });
391
+ await Promise.all(batchedPromises);
392
+ totalDocumentsTransferred += fromCollDocs.documents.length;
393
+ }
394
+ }
395
+ console.log(
396
+ `Total documents transferred from database ${fromDbId} to database ${toDbId} -- collection ${fromCollId} to collection ${toCollId}: ${totalDocumentsTransferred}`
397
+ );
398
+ };
399
+
400
+ /**
401
+ * Transfers all collections and documents from one local database to another local database.
402
+ *
403
+ * @param {Databases} localDb - The local database instance.
404
+ * @param {string} fromDbId - The ID of the source database.
405
+ * @param {string} targetDbId - The ID of the target database.
406
+ * @return {Promise<void>} A promise that resolves when the transfer is complete.
407
+ */
408
+ export const transferDatabaseLocalToLocal = async (
409
+ localDb: Databases,
410
+ fromDbId: string,
411
+ targetDbId: string
412
+ ) => {
413
+ let lastCollectionId: string | undefined;
414
+ let fromCollections = await tryAwaitWithRetry(
415
+ async () => await localDb.listCollections(fromDbId, [Query.limit(50)])
416
+ );
417
+ const allFromCollections = fromCollections.collections;
418
+ if (fromCollections.collections.length < 50) {
419
+ lastCollectionId = undefined;
420
+ } else {
421
+ lastCollectionId =
422
+ fromCollections.collections[fromCollections.collections.length - 1].$id;
423
+ while (lastCollectionId) {
424
+ const collections = await localDb.listCollections(fromDbId, [
425
+ Query.limit(50),
426
+ Query.cursorAfter(lastCollectionId),
427
+ ]);
428
+ allFromCollections.push(...collections.collections);
429
+ if (collections.collections.length < 50) {
430
+ break;
431
+ }
432
+ lastCollectionId =
433
+ collections.collections[collections.collections.length - 1].$id;
434
+ }
435
+ }
436
+ lastCollectionId = undefined;
437
+ let toCollections = await tryAwaitWithRetry(
438
+ async () => await localDb.listCollections(targetDbId, [Query.limit(50)])
439
+ );
440
+ const allToCollections = toCollections.collections;
441
+ if (toCollections.collections.length < 50) {
442
+ } else {
443
+ lastCollectionId =
444
+ toCollections.collections[toCollections.collections.length - 1].$id;
445
+ while (lastCollectionId) {
446
+ const collections = await localDb.listCollections(targetDbId, [
447
+ Query.limit(50),
448
+ Query.cursorAfter(lastCollectionId),
449
+ ]);
450
+ allToCollections.push(...collections.collections);
451
+ if (collections.collections.length < 50) {
452
+ lastCollectionId = undefined;
453
+ } else {
454
+ lastCollectionId =
455
+ collections.collections[collections.collections.length - 1].$id;
456
+ }
457
+ }
458
+ }
459
+ for (const collection of allFromCollections) {
460
+ const toCollection = allToCollections.find((c) => c.$id === collection.$id);
461
+ if (toCollection) {
462
+ await transferDocumentsBetweenDbsLocalToLocal(
463
+ localDb,
464
+ fromDbId,
465
+ targetDbId,
466
+ collection.$id,
467
+ toCollection.$id
468
+ );
469
+ } else {
470
+ console.log(
471
+ `Collection ${collection.name} not found in destination database, creating...`
472
+ );
473
+ const newCollection = await tryAwaitWithRetry(
474
+ async () =>
475
+ await localDb.createCollection(
476
+ targetDbId,
477
+ collection.$id,
478
+ collection.name,
479
+ collection.$permissions,
480
+ collection.documentSecurity,
481
+ collection.enabled
482
+ )
483
+ );
484
+ console.log(`Collection ${newCollection.name} created`);
485
+ for (const attribute of collection.attributes) {
486
+ await tryAwaitWithRetry(
487
+ async () =>
488
+ await createOrUpdateAttribute(
489
+ localDb,
490
+ targetDbId,
491
+ newCollection,
492
+ parseAttribute(attribute as any)
493
+ )
494
+ );
495
+ }
496
+ for (const index of collection.indexes) {
497
+ await tryAwaitWithRetry(
498
+ async () =>
499
+ await localDb.createIndex(
500
+ targetDbId,
501
+ newCollection.$id,
502
+ index.key,
503
+ index.type as IndexType,
504
+ index.attributes,
505
+ index.orders
506
+ )
507
+ );
508
+ }
509
+ await transferDocumentsBetweenDbsLocalToLocal(
510
+ localDb,
511
+ fromDbId,
512
+ targetDbId,
513
+ collection.$id,
514
+ newCollection.$id
515
+ );
516
+ }
517
+ }
518
+ };
519
+
520
+ export const transferDatabaseLocalToRemote = async (
521
+ localDb: Databases,
522
+ endpoint: string,
523
+ projectId: string,
524
+ apiKey: string,
525
+ fromDbId: string,
526
+ toDbId: string
527
+ ) => {
528
+ const client = getAppwriteClient(endpoint, projectId, apiKey);
529
+ const remoteDb = new Databases(client);
530
+
531
+ let lastCollectionId: string | undefined;
532
+ let fromCollections = await tryAwaitWithRetry(
533
+ async () => await localDb.listCollections(fromDbId, [Query.limit(50)])
534
+ );
535
+ const allFromCollections = fromCollections.collections;
536
+ if (fromCollections.collections.length < 50) {
537
+ } else {
538
+ lastCollectionId =
539
+ fromCollections.collections[fromCollections.collections.length - 1].$id;
540
+ while (lastCollectionId) {
541
+ const collections = await tryAwaitWithRetry(
542
+ async () =>
543
+ await localDb.listCollections(fromDbId, [
544
+ Query.limit(50),
545
+ Query.cursorAfter(lastCollectionId!),
546
+ ])
547
+ );
548
+ allFromCollections.push(...collections.collections);
549
+ if (collections.collections.length < 50) {
550
+ break;
551
+ }
552
+ lastCollectionId =
553
+ collections.collections[collections.collections.length - 1].$id;
554
+ }
555
+ }
556
+
557
+ for (const collection of allFromCollections) {
558
+ const toCollection = await tryAwaitWithRetry(
559
+ async () =>
560
+ await remoteDb.createCollection(
561
+ toDbId,
562
+ collection.$id,
563
+ collection.name,
564
+ collection.$permissions,
565
+ collection.documentSecurity,
566
+ collection.enabled
567
+ )
568
+ );
569
+ console.log(`Collection ${toCollection.name} created`);
570
+
571
+ for (const attribute of collection.attributes) {
572
+ await tryAwaitWithRetry(
573
+ async () =>
574
+ await createOrUpdateAttribute(
575
+ remoteDb,
576
+ toDbId,
577
+ toCollection,
578
+ parseAttribute(attribute as any)
579
+ )
580
+ );
581
+ }
582
+
583
+ for (const index of collection.indexes) {
584
+ await tryAwaitWithRetry(
585
+ async () =>
586
+ await remoteDb.createIndex(
587
+ toDbId,
588
+ toCollection.$id,
589
+ index.key,
590
+ index.type as IndexType,
591
+ index.attributes,
592
+ index.orders
593
+ )
594
+ );
595
+ }
596
+
597
+ await transferDocumentsBetweenDbsLocalToRemote(
598
+ localDb,
599
+ endpoint,
600
+ projectId,
601
+ apiKey,
602
+ fromDbId,
603
+ toDbId,
604
+ collection.$id,
605
+ toCollection.$id
606
+ );
607
+ }
608
+ };