appwrite-utils-cli 0.9.77 → 0.9.79
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 +3 -0
- package/dist/collections/attributes.js +12 -5
- package/dist/collections/methods.d.ts +2 -1
- package/dist/collections/methods.js +68 -36
- package/dist/interactiveCLI.d.ts +4 -0
- package/dist/interactiveCLI.js +141 -53
- package/dist/main.js +53 -39
- package/dist/migrations/importController.d.ts +3 -3
- package/dist/migrations/importController.js +126 -149
- package/dist/migrations/setupDatabase.d.ts +2 -1
- package/dist/migrations/setupDatabase.js +27 -5
- package/dist/storage/methods.js +4 -1
- package/dist/utils/setupFiles.js +4 -1
- package/dist/utilsController.d.ts +8 -4
- package/dist/utilsController.js +45 -15
- package/package.json +4 -3
- package/src/collections/attributes.ts +12 -7
- package/src/collections/methods.ts +78 -40
- package/src/interactiveCLI.ts +179 -75
- package/src/main.ts +60 -45
- package/src/migrations/importController.ts +167 -279
- package/src/migrations/setupDatabase.ts +48 -7
- package/src/storage/methods.ts +4 -4
- package/src/utils/setupFiles.ts +4 -1
- package/src/utilsController.ts +50 -13
package/dist/main.js
CHANGED
@@ -6,6 +6,9 @@ import { InteractiveCLI } from "./interactiveCLI.js";
|
|
6
6
|
import { UtilsController } from "./utilsController.js";
|
7
7
|
import { Databases, Storage } from "node-appwrite";
|
8
8
|
import { getClient } from "./utils/getClientFromConfig.js";
|
9
|
+
import { fetchAllDatabases } from "./migrations/databases.js";
|
10
|
+
import { setupDirsFiles } from "./utils/setupFiles.js";
|
11
|
+
import { fetchAllCollections } from "./collections/methods.js";
|
9
12
|
const argv = yargs(hideBin(process.argv))
|
10
13
|
.option("it", {
|
11
14
|
alias: ["interactive", "i"],
|
@@ -28,6 +31,10 @@ const argv = yargs(hideBin(process.argv))
|
|
28
31
|
.option("wipe", {
|
29
32
|
choices: ["all", "docs", "users"],
|
30
33
|
description: "Wipe data (all: everything, docs: only documents, users: only user data)",
|
34
|
+
})
|
35
|
+
.option("wipeCollections", {
|
36
|
+
type: "boolean",
|
37
|
+
description: "Wipe collections, uses collectionIds option to get the collections to wipe",
|
31
38
|
})
|
32
39
|
.option("generate", {
|
33
40
|
type: "boolean",
|
@@ -90,78 +97,79 @@ const argv = yargs(hideBin(process.argv))
|
|
90
97
|
description: "Set the destination collection ID for transfer",
|
91
98
|
})
|
92
99
|
.option("fromBucketId", {
|
93
|
-
alias: ["fromBucket"],
|
94
100
|
type: "string",
|
95
101
|
description: "Set the source bucket ID for transfer",
|
96
102
|
})
|
97
103
|
.option("toBucketId", {
|
98
|
-
alias: ["toBucket"],
|
99
104
|
type: "string",
|
100
105
|
description: "Set the destination bucket ID for transfer",
|
101
106
|
})
|
102
107
|
.option("remoteEndpoint", {
|
103
108
|
type: "string",
|
104
|
-
description: "Set the remote Appwrite endpoint for
|
109
|
+
description: "Set the remote Appwrite endpoint for transfer",
|
105
110
|
})
|
106
111
|
.option("remoteProjectId", {
|
107
112
|
type: "string",
|
108
|
-
description: "Set the remote Appwrite project ID for
|
113
|
+
description: "Set the remote Appwrite project ID for transfer",
|
109
114
|
})
|
110
115
|
.option("remoteApiKey", {
|
111
116
|
type: "string",
|
112
|
-
description: "Set the remote Appwrite API key for
|
117
|
+
description: "Set the remote Appwrite API key for transfer",
|
113
118
|
})
|
114
119
|
.option("setup", {
|
115
120
|
type: "boolean",
|
116
|
-
description: "Setup
|
121
|
+
description: "Setup directories and files",
|
117
122
|
})
|
118
|
-
.help()
|
119
123
|
.parse();
|
120
124
|
async function main() {
|
121
|
-
const
|
122
|
-
|
123
|
-
if (parsedArgv.it) {
|
124
|
-
try {
|
125
|
-
controller = new UtilsController(process.cwd());
|
126
|
-
await controller.init();
|
127
|
-
}
|
128
|
-
catch (error) {
|
129
|
-
// If it fails, that means there's no config, more than likely
|
130
|
-
console.log("No config found, you probably need to create the setup files");
|
131
|
-
}
|
125
|
+
const controller = new UtilsController(process.cwd());
|
126
|
+
if (argv.it) {
|
132
127
|
const cli = new InteractiveCLI(process.cwd());
|
133
128
|
await cli.run();
|
134
129
|
}
|
135
130
|
else {
|
136
|
-
|
137
|
-
|
138
|
-
await
|
131
|
+
await controller.init();
|
132
|
+
if (argv.setup) {
|
133
|
+
await setupDirsFiles(false, process.cwd());
|
134
|
+
return;
|
139
135
|
}
|
140
|
-
|
136
|
+
const parsedArgv = argv;
|
141
137
|
const options = {
|
142
138
|
databases: parsedArgv.dbIds
|
143
|
-
? await controller.getDatabasesByIds(parsedArgv.dbIds.
|
144
|
-
: undefined,
|
145
|
-
collections: parsedArgv.collectionIds
|
146
|
-
? parsedArgv.collectionIds.replace(" ", "").split(",")
|
139
|
+
? await controller.getDatabasesByIds(parsedArgv.dbIds.split(","))
|
147
140
|
: undefined,
|
141
|
+
collections: parsedArgv.collectionIds?.split(","),
|
148
142
|
doBackup: parsedArgv.backup,
|
149
143
|
wipeDatabase: parsedArgv.wipe === "all" || parsedArgv.wipe === "docs",
|
150
144
|
wipeDocumentStorage: parsedArgv.wipe === "all",
|
151
145
|
wipeUsers: parsedArgv.wipe === "all" || parsedArgv.wipe === "users",
|
152
146
|
generateSchemas: parsedArgv.generate,
|
153
147
|
importData: parsedArgv.import,
|
154
|
-
checkDuplicates: false,
|
155
148
|
shouldWriteFile: parsedArgv.writeData,
|
149
|
+
wipeCollections: parsedArgv.wipeCollections,
|
156
150
|
};
|
157
|
-
if (parsedArgv.push) {
|
158
|
-
await controller.
|
151
|
+
if (parsedArgv.push || parsedArgv.sync) {
|
152
|
+
const databases = options.databases || await fetchAllDatabases(controller.database);
|
153
|
+
let collections = [];
|
154
|
+
if (options.collections) {
|
155
|
+
for (const db of databases) {
|
156
|
+
const dbCollections = await fetchAllCollections(db.$id, controller.database);
|
157
|
+
collections = collections.concat(dbCollections.filter(c => options.collections.includes(c.$id)));
|
158
|
+
}
|
159
|
+
}
|
160
|
+
if (parsedArgv.push) {
|
161
|
+
await controller.syncDb(databases, collections);
|
162
|
+
}
|
163
|
+
else if (parsedArgv.sync) {
|
164
|
+
await controller.synchronizeConfigurations(databases);
|
165
|
+
}
|
159
166
|
}
|
160
167
|
if (options.wipeDatabase ||
|
161
168
|
options.wipeDocumentStorage ||
|
162
|
-
options.wipeUsers
|
163
|
-
|
164
|
-
|
169
|
+
options.wipeUsers ||
|
170
|
+
options.wipeCollections) {
|
171
|
+
if (options.wipeDatabase && options.databases) {
|
172
|
+
for (const db of options.databases) {
|
165
173
|
await controller.wipeDatabase(db);
|
166
174
|
}
|
167
175
|
}
|
@@ -173,9 +181,18 @@ async function main() {
|
|
173
181
|
if (options.wipeUsers) {
|
174
182
|
await controller.wipeUsers();
|
175
183
|
}
|
184
|
+
if (options.wipeCollections && options.databases) {
|
185
|
+
for (const db of options.databases) {
|
186
|
+
const dbCollections = await fetchAllCollections(db.$id, controller.database);
|
187
|
+
const collectionsToWipe = dbCollections.filter(c => options.collections.includes(c.$id));
|
188
|
+
for (const collection of collectionsToWipe) {
|
189
|
+
await controller.wipeCollection(db, collection);
|
190
|
+
}
|
191
|
+
}
|
192
|
+
}
|
176
193
|
}
|
177
|
-
if (options.doBackup) {
|
178
|
-
for (const db of options.databases
|
194
|
+
if (options.doBackup && options.databases) {
|
195
|
+
for (const db of options.databases) {
|
179
196
|
await controller.backupDatabase(db);
|
180
197
|
}
|
181
198
|
}
|
@@ -200,8 +217,8 @@ async function main() {
|
|
200
217
|
const remoteClient = getClient(parsedArgv.remoteEndpoint, parsedArgv.remoteProjectId, parsedArgv.remoteApiKey);
|
201
218
|
targetDatabases = new Databases(remoteClient);
|
202
219
|
targetStorage = new Storage(remoteClient);
|
203
|
-
const remoteDbs = await targetDatabases
|
204
|
-
toDb = remoteDbs.
|
220
|
+
const remoteDbs = await fetchAllDatabases(targetDatabases);
|
221
|
+
toDb = remoteDbs.find((db) => db.$id === parsedArgv.toDbId);
|
205
222
|
}
|
206
223
|
else {
|
207
224
|
toDb = (await controller.getDatabasesByIds([parsedArgv.toDbId]))[0];
|
@@ -233,9 +250,6 @@ async function main() {
|
|
233
250
|
};
|
234
251
|
await controller.transferData(transferOptions);
|
235
252
|
}
|
236
|
-
if (parsedArgv.sync) {
|
237
|
-
await controller.synchronizeConfigurations(options.databases);
|
238
|
-
}
|
239
253
|
}
|
240
254
|
}
|
241
255
|
main().catch((error) => {
|
@@ -16,8 +16,8 @@ export declare class ImportController {
|
|
16
16
|
private postImportActionsQueue;
|
17
17
|
private databasesToRun;
|
18
18
|
constructor(config: AppwriteConfig, database: Databases, storage: Storage, appwriteFolderPath: string, importDataActions: ImportDataActions, setupOptions: SetupOptions, databasesToRun?: Models.Database[]);
|
19
|
-
run(): Promise<void>;
|
19
|
+
run(specificCollections?: string[]): Promise<void>;
|
20
20
|
updateOthersToFinalData(updatedDb: Models.Database, targetDb: Models.Database): Promise<void>;
|
21
|
-
importCollections(db: ConfigDatabase, dataLoader: DataLoader): Promise<void>;
|
22
|
-
executePostImportActions(dbId: string, dataLoader: DataLoader): Promise<void>;
|
21
|
+
importCollections(db: ConfigDatabase, dataLoader: DataLoader, specificCollections?: string[]): Promise<void>;
|
22
|
+
executePostImportActions(dbId: string, dataLoader: DataLoader, specificCollections?: string[]): Promise<void>;
|
23
23
|
}
|
@@ -30,7 +30,7 @@ export class ImportController {
|
|
30
30
|
this.documentCache = new Map();
|
31
31
|
this.databasesToRun = databasesToRun || [];
|
32
32
|
}
|
33
|
-
async run() {
|
33
|
+
async run(specificCollections) {
|
34
34
|
let databasesToProcess;
|
35
35
|
if (this.databasesToRun.length > 0) {
|
36
36
|
// Use the provided databases
|
@@ -54,9 +54,9 @@ export class ImportController {
|
|
54
54
|
databaseRan = db;
|
55
55
|
dataLoader = new DataLoader(this.appwriteFolderPath, this.importDataActions, this.database, this.config, this.setupOptions.shouldWriteFile);
|
56
56
|
await dataLoader.start(db.$id);
|
57
|
-
await this.importCollections(db, dataLoader);
|
57
|
+
await this.importCollections(db, dataLoader, specificCollections);
|
58
58
|
await resolveAndUpdateRelationships(db.$id, this.database, this.config);
|
59
|
-
await this.executePostImportActions(db.$id, dataLoader);
|
59
|
+
await this.executePostImportActions(db.$id, dataLoader, specificCollections);
|
60
60
|
}
|
61
61
|
else if (databaseRan.$id !== db.$id) {
|
62
62
|
await this.updateOthersToFinalData(databaseRan, db);
|
@@ -76,170 +76,147 @@ export class ImportController {
|
|
76
76
|
await transferStorageLocalToLocal(this.storage, updatedDbConfig.bucket.$id, targetDbConfig.bucket.$id);
|
77
77
|
}
|
78
78
|
}
|
79
|
-
async importCollections(db, dataLoader) {
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
dataLoader.getCollectionKey(collection.name);
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
79
|
+
async importCollections(db, dataLoader, specificCollections) {
|
80
|
+
const collectionsToImport = specificCollections || (this.config.collections ? this.config.collections.map(c => c.name) : []);
|
81
|
+
for (const collection of this.config.collections || []) {
|
82
|
+
if (collectionsToImport.includes(collection.name)) {
|
83
|
+
let isUsersCollection = dataLoader.getCollectionKey(this.config.usersCollectionName) ===
|
84
|
+
dataLoader.getCollectionKey(collection.name);
|
85
|
+
const importOperationId = dataLoader.collectionImportOperations.get(dataLoader.getCollectionKey(collection.name));
|
86
|
+
const createBatches = (finalData) => {
|
87
|
+
let maxBatchLength = 100;
|
88
|
+
const finalBatches = [];
|
89
|
+
for (let i = 0; i < finalData.length; i++) {
|
90
|
+
if (i % maxBatchLength === 0) {
|
91
|
+
finalBatches.push([]);
|
92
|
+
}
|
93
|
+
finalBatches[finalBatches.length - 1].push(finalData[i]);
|
93
94
|
}
|
94
|
-
finalBatches
|
95
|
-
}
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
itemId
|
115
|
-
|
116
|
-
|
117
|
-
return
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
})
|
123
|
-
.map((item) => {
|
124
|
-
dataLoader.userExistsMap.set(item.finalData.userId ||
|
125
|
-
item.finalData.docId ||
|
126
|
-
item.context.userId ||
|
127
|
-
item.context.docId, true);
|
128
|
-
return usersController.createUserAndReturn(item.finalData);
|
129
|
-
});
|
130
|
-
const promiseResults = await Promise.allSettled(userBatchPromises);
|
131
|
-
for (const item of batch) {
|
132
|
-
if (item && item.finalData) {
|
95
|
+
return finalBatches;
|
96
|
+
};
|
97
|
+
if (isUsersCollection && !this.hasImportedUsers) {
|
98
|
+
const usersDataMap = dataLoader.importMap.get(dataLoader.getCollectionKey("users"));
|
99
|
+
const usersData = usersDataMap?.data;
|
100
|
+
const usersController = new UsersController(this.config, this.database);
|
101
|
+
if (usersData) {
|
102
|
+
console.log("Found users data", usersData.length);
|
103
|
+
const userDataBatches = createBatches(usersData);
|
104
|
+
for (const batch of userDataBatches) {
|
105
|
+
console.log("Importing users batch", batch.length);
|
106
|
+
const userBatchPromises = batch
|
107
|
+
.filter((item) => {
|
108
|
+
let itemId;
|
109
|
+
if (item.finalData.userId) {
|
110
|
+
itemId = item.finalData.userId;
|
111
|
+
}
|
112
|
+
else if (item.finalData.docId) {
|
113
|
+
itemId = item.finalData.docId;
|
114
|
+
}
|
115
|
+
if (!itemId) {
|
116
|
+
return false;
|
117
|
+
}
|
118
|
+
return (item &&
|
119
|
+
item.finalData &&
|
120
|
+
!dataLoader.userExistsMap.has(itemId));
|
121
|
+
})
|
122
|
+
.map((item) => {
|
133
123
|
dataLoader.userExistsMap.set(item.finalData.userId ||
|
134
124
|
item.finalData.docId ||
|
135
125
|
item.context.userId ||
|
136
126
|
item.context.docId, true);
|
127
|
+
return usersController.createUserAndReturn(item.finalData);
|
128
|
+
});
|
129
|
+
const promiseResults = await Promise.allSettled(userBatchPromises);
|
130
|
+
for (const item of batch) {
|
131
|
+
if (item && item.finalData) {
|
132
|
+
dataLoader.userExistsMap.set(item.finalData.userId ||
|
133
|
+
item.finalData.docId ||
|
134
|
+
item.context.userId ||
|
135
|
+
item.context.docId, true);
|
136
|
+
}
|
137
137
|
}
|
138
|
+
console.log("Finished importing users batch");
|
138
139
|
}
|
139
|
-
|
140
|
+
this.hasImportedUsers = true;
|
141
|
+
console.log("Finished importing users");
|
140
142
|
}
|
141
|
-
this.hasImportedUsers = true;
|
142
|
-
console.log("Finished importing users");
|
143
143
|
}
|
144
|
-
|
145
|
-
|
146
|
-
|
147
|
-
|
148
|
-
|
149
|
-
|
150
|
-
|
151
|
-
|
152
|
-
|
153
|
-
|
154
|
-
|
155
|
-
|
156
|
-
|
157
|
-
|
158
|
-
|
159
|
-
|
160
|
-
|
161
|
-
|
162
|
-
|
163
|
-
|
164
|
-
|
165
|
-
|
166
|
-
|
167
|
-
|
168
|
-
|
169
|
-
|
170
|
-
|
171
|
-
|
172
|
-
|
173
|
-
|
174
|
-
|
175
|
-
|
176
|
-
|
177
|
-
|
178
|
-
|
179
|
-
// collection.$id,
|
180
|
-
// item.finalData
|
181
|
-
// )
|
182
|
-
// );
|
183
|
-
// } catch (error) {
|
184
|
-
// console.error(error);
|
185
|
-
// return Promise.resolve(null);
|
186
|
-
// }
|
187
|
-
// });
|
188
|
-
// const documentExistsResults = await Promise.all(documentExistsPromises);
|
189
|
-
const batchPromises = batches.map((item, index) => {
|
190
|
-
try {
|
191
|
-
const id = item.finalData.docId ||
|
192
|
-
item.finalData.userId ||
|
193
|
-
item.context.docId ||
|
194
|
-
item.context.userId;
|
195
|
-
if (item.finalData.hasOwnProperty("userId")) {
|
196
|
-
delete item.finalData.userId;
|
197
|
-
}
|
198
|
-
if (item.finalData.hasOwnProperty("docId")) {
|
199
|
-
delete item.finalData.docId;
|
144
|
+
if (!importOperationId) {
|
145
|
+
// Skip further processing if no import operation is found
|
146
|
+
continue;
|
147
|
+
}
|
148
|
+
const importOperation = await this.database.getDocument("migrations", "currentOperations", importOperationId);
|
149
|
+
await updateOperation(this.database, importOperation.$id, {
|
150
|
+
status: "in_progress",
|
151
|
+
});
|
152
|
+
const collectionData = dataLoader.importMap.get(dataLoader.getCollectionKey(collection.name));
|
153
|
+
console.log(`Processing collection: ${collection.name}...`);
|
154
|
+
if (!collectionData) {
|
155
|
+
console.log("No collection data for ", collection.name);
|
156
|
+
continue;
|
157
|
+
}
|
158
|
+
const dataSplit = createBatches(collectionData.data);
|
159
|
+
let processedItems = 0;
|
160
|
+
for (let i = 0; i < dataSplit.length; i++) {
|
161
|
+
const batches = dataSplit[i];
|
162
|
+
console.log(`Processing batch ${i + 1} of ${dataSplit.length}`);
|
163
|
+
const batchPromises = batches.map((item, index) => {
|
164
|
+
try {
|
165
|
+
const id = item.finalData.docId ||
|
166
|
+
item.finalData.userId ||
|
167
|
+
item.context.docId ||
|
168
|
+
item.context.userId;
|
169
|
+
if (item.finalData.hasOwnProperty("userId")) {
|
170
|
+
delete item.finalData.userId;
|
171
|
+
}
|
172
|
+
if (item.finalData.hasOwnProperty("docId")) {
|
173
|
+
delete item.finalData.docId;
|
174
|
+
}
|
175
|
+
if (!item.finalData) {
|
176
|
+
return Promise.resolve();
|
177
|
+
}
|
178
|
+
return tryAwaitWithRetry(async () => await this.database.createDocument(db.$id, collection.$id, id, item.finalData));
|
200
179
|
}
|
201
|
-
|
180
|
+
catch (error) {
|
181
|
+
console.error(error);
|
202
182
|
return Promise.resolve();
|
203
183
|
}
|
204
|
-
|
205
|
-
|
206
|
-
|
207
|
-
|
208
|
-
|
209
|
-
|
210
|
-
|
211
|
-
|
212
|
-
|
213
|
-
console.log(`Completed batch ${i + 1} of ${dataSplit.length}`);
|
184
|
+
});
|
185
|
+
// Wait for all promises in the current batch to resolve
|
186
|
+
await Promise.all(batchPromises);
|
187
|
+
console.log(`Completed batch ${i + 1} of ${dataSplit.length}`);
|
188
|
+
await updateOperation(this.database, importOperation.$id, {
|
189
|
+
progress: processedItems,
|
190
|
+
});
|
191
|
+
}
|
192
|
+
// After all batches are processed, update the operation status to completed
|
214
193
|
await updateOperation(this.database, importOperation.$id, {
|
215
|
-
|
194
|
+
status: "completed",
|
216
195
|
});
|
217
196
|
}
|
218
|
-
// After all batches are processed, update the operation status to completed
|
219
|
-
await updateOperation(this.database, importOperation.$id, {
|
220
|
-
status: "completed",
|
221
|
-
});
|
222
197
|
}
|
223
198
|
}
|
224
|
-
async executePostImportActions(dbId, dataLoader) {
|
199
|
+
async executePostImportActions(dbId, dataLoader, specificCollections) {
|
200
|
+
const collectionsToProcess = specificCollections || Array.from(dataLoader.importMap.keys());
|
225
201
|
// Iterate over each collection in the importMap
|
226
|
-
for (const [collectionKey, collectionData
|
227
|
-
|
228
|
-
|
229
|
-
|
230
|
-
|
231
|
-
|
232
|
-
|
233
|
-
|
234
|
-
|
235
|
-
|
236
|
-
|
237
|
-
|
238
|
-
|
239
|
-
|
240
|
-
|
241
|
-
|
242
|
-
|
202
|
+
for (const [collectionKey, collectionData] of dataLoader.importMap.entries()) {
|
203
|
+
if (collectionsToProcess.includes(collectionKey)) {
|
204
|
+
console.log(`Processing post-import actions for collection: ${collectionKey}`);
|
205
|
+
// Iterate over each item in the collectionData.data
|
206
|
+
for (const item of collectionData.data) {
|
207
|
+
// Assuming each item has attributeMappings that contain actions to be executed
|
208
|
+
if (item.importDef && item.importDef.attributeMappings) {
|
209
|
+
// Use item.context as the context for action execution
|
210
|
+
const context = item.context; // Directly use item.context as the context for action execution
|
211
|
+
// Iterate through attributeMappings to execute actions
|
212
|
+
try {
|
213
|
+
// Execute post-import actions for the current attributeMapping
|
214
|
+
// Pass item.finalData as the data to be processed along with the context
|
215
|
+
await this.importDataActions.executeAfterImportActions(item.finalData, item.importDef.attributeMappings, context);
|
216
|
+
}
|
217
|
+
catch (error) {
|
218
|
+
console.error(`Failed to execute post-import actions for item in collection ${collectionKey}:`, error);
|
219
|
+
}
|
243
220
|
}
|
244
221
|
}
|
245
222
|
}
|
@@ -1,5 +1,6 @@
|
|
1
1
|
import { Databases, type Models } from "node-appwrite";
|
2
2
|
import { type AppwriteConfig } from "appwrite-utils";
|
3
3
|
export declare const setupMigrationDatabase: (config: AppwriteConfig) => Promise<void>;
|
4
|
-
export declare const ensureDatabasesExist: (config: AppwriteConfig) => Promise<void>;
|
4
|
+
export declare const ensureDatabasesExist: (config: AppwriteConfig, databasesToEnsure?: Models.Database[]) => Promise<void>;
|
5
5
|
export declare const wipeOtherDatabases: (database: Databases, databasesToKeep: Models.Database[]) => Promise<void>;
|
6
|
+
export declare const ensureCollectionsExist: (config: AppwriteConfig, database: Models.Database, collectionsToEnsure?: Models.Collection[]) => Promise<void>;
|
@@ -9,6 +9,9 @@ export const setupMigrationDatabase = async (config) => {
|
|
9
9
|
console.log("Starting Migrations Setup");
|
10
10
|
console.log("---------------------------------");
|
11
11
|
const database = new Databases(config.appwriteClient);
|
12
|
+
if (!config.appwriteClient) {
|
13
|
+
throw new Error("Appwrite client is not initialized in the config");
|
14
|
+
}
|
12
15
|
let db;
|
13
16
|
const migrationCollectionsSetup = getMigrationCollectionSchemas();
|
14
17
|
try {
|
@@ -60,16 +63,23 @@ export const setupMigrationDatabase = async (config) => {
|
|
60
63
|
console.log("Migrations Setup Complete");
|
61
64
|
console.log("---------------------------------");
|
62
65
|
};
|
63
|
-
export const ensureDatabasesExist = async (config) => {
|
66
|
+
export const ensureDatabasesExist = async (config, databasesToEnsure) => {
|
67
|
+
if (!config.appwriteClient) {
|
68
|
+
throw new Error("Appwrite client is not initialized in the config");
|
69
|
+
}
|
64
70
|
const database = new Databases(config.appwriteClient);
|
65
|
-
const
|
71
|
+
const databasesToCreate = databasesToEnsure || config.databases || [];
|
72
|
+
if (!databasesToCreate.length) {
|
73
|
+
console.log("No databases to create");
|
74
|
+
return;
|
75
|
+
}
|
66
76
|
const existingDatabases = await tryAwaitWithRetry(async () => await database.list([Query.limit(500)]));
|
67
77
|
const migrationsDatabase = existingDatabases.databases.find((d) => d.name.toLowerCase().trim().replace(" ", "") === "migrations");
|
68
78
|
if (existingDatabases.databases.length !== 0 && migrationsDatabase) {
|
69
79
|
console.log("Creating all databases except migrations");
|
70
|
-
|
80
|
+
databasesToCreate.push(migrationsDatabase);
|
71
81
|
}
|
72
|
-
for (const db of
|
82
|
+
for (const db of databasesToCreate) {
|
73
83
|
if (!existingDatabases.databases.some((d) => d.name === db.name)) {
|
74
84
|
await tryAwaitWithRetry(async () => await database.create(db.$id || ulid(), db.name, true));
|
75
85
|
console.log(`${db.name} database created`);
|
@@ -77,7 +87,7 @@ export const ensureDatabasesExist = async (config) => {
|
|
77
87
|
}
|
78
88
|
};
|
79
89
|
export const wipeOtherDatabases = async (database, databasesToKeep) => {
|
80
|
-
console.log(`Databases to keep: ${databasesToKeep.join(", ")}`);
|
90
|
+
console.log(`Databases to keep: ${databasesToKeep.map(db => db.name).join(", ")}`);
|
81
91
|
const allDatabases = await tryAwaitWithRetry(async () => await database.list([Query.limit(500)]));
|
82
92
|
const migrationsDatabase = allDatabases.databases.find((d) => d.name.toLowerCase().trim().replace(" ", "") === "migrations");
|
83
93
|
if (allDatabases.databases.length !== 0 && migrationsDatabase) {
|
@@ -91,3 +101,15 @@ export const wipeOtherDatabases = async (database, databasesToKeep) => {
|
|
91
101
|
}
|
92
102
|
}
|
93
103
|
};
|
104
|
+
export const ensureCollectionsExist = async (config, database, collectionsToEnsure) => {
|
105
|
+
const databaseClient = new Databases(config.appwriteClient);
|
106
|
+
const collectionsToCreate = collectionsToEnsure ||
|
107
|
+
(config.collections ? config.collections : []);
|
108
|
+
const existingCollections = await tryAwaitWithRetry(async () => await databaseClient.listCollections(database.$id, [Query.limit(500)]));
|
109
|
+
for (const collection of collectionsToCreate) {
|
110
|
+
if (!existingCollections.collections.some((c) => c.name === collection.name)) {
|
111
|
+
await tryAwaitWithRetry(async () => await databaseClient.createCollection(database.$id, ulid(), collection.name, undefined, true, true));
|
112
|
+
console.log(`${collection.name} collection created in ${database.name}`);
|
113
|
+
}
|
114
|
+
}
|
115
|
+
};
|
package/dist/storage/methods.js
CHANGED
@@ -74,7 +74,10 @@ export const ensureDatabaseConfigBucketsExist = async (storage, config, database
|
|
74
74
|
console.log(`Bucket ${database.bucket.$id} created successfully.`);
|
75
75
|
}
|
76
76
|
catch (createError) {
|
77
|
-
console.error(
|
77
|
+
// console.error(
|
78
|
+
// `Failed to create bucket ${database.bucket.$id}:`,
|
79
|
+
// createError
|
80
|
+
// );
|
78
81
|
}
|
79
82
|
}
|
80
83
|
}
|
package/dist/utils/setupFiles.js
CHANGED
@@ -71,10 +71,13 @@ const baseConfig = {
|
|
71
71
|
const collectionsConfig = [
|
72
72
|
{
|
73
73
|
name: "ExampleCollection",
|
74
|
-
content: `import { CollectionCreate } from "appwrite-utils";
|
74
|
+
content: `import type { CollectionCreate } from "appwrite-utils";
|
75
75
|
|
76
76
|
const ExampleCollection: Partial<CollectionCreate> = {
|
77
77
|
name: 'ExampleCollection',
|
78
|
+
$id: '${ulid()}',
|
79
|
+
documentSecurity: false,
|
80
|
+
enabled: true,
|
78
81
|
$permissions: [
|
79
82
|
{ permission: 'read', target: 'any' },
|
80
83
|
{ permission: 'create', target: 'users' },
|