appwrite-utils-cli 0.0.1
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 +80 -0
- package/dist/main.d.ts +2 -0
- package/dist/main.js +74 -0
- package/dist/migrations/afterImportActions.d.ts +12 -0
- package/dist/migrations/afterImportActions.js +196 -0
- package/dist/migrations/attributes.d.ts +4 -0
- package/dist/migrations/attributes.js +158 -0
- package/dist/migrations/backup.d.ts +621 -0
- package/dist/migrations/backup.js +159 -0
- package/dist/migrations/collections.d.ts +16 -0
- package/dist/migrations/collections.js +207 -0
- package/dist/migrations/converters.d.ts +179 -0
- package/dist/migrations/converters.js +575 -0
- package/dist/migrations/dbHelpers.d.ts +5 -0
- package/dist/migrations/dbHelpers.js +54 -0
- package/dist/migrations/importController.d.ts +44 -0
- package/dist/migrations/importController.js +312 -0
- package/dist/migrations/importDataActions.d.ts +44 -0
- package/dist/migrations/importDataActions.js +219 -0
- package/dist/migrations/indexes.d.ts +4 -0
- package/dist/migrations/indexes.js +18 -0
- package/dist/migrations/logging.d.ts +2 -0
- package/dist/migrations/logging.js +14 -0
- package/dist/migrations/migrationHelper.d.ts +18 -0
- package/dist/migrations/migrationHelper.js +66 -0
- package/dist/migrations/queue.d.ts +13 -0
- package/dist/migrations/queue.js +79 -0
- package/dist/migrations/relationships.d.ts +90 -0
- package/dist/migrations/relationships.js +209 -0
- package/dist/migrations/schema.d.ts +3142 -0
- package/dist/migrations/schema.js +485 -0
- package/dist/migrations/schemaStrings.d.ts +12 -0
- package/dist/migrations/schemaStrings.js +261 -0
- package/dist/migrations/setupDatabase.d.ts +7 -0
- package/dist/migrations/setupDatabase.js +151 -0
- package/dist/migrations/storage.d.ts +8 -0
- package/dist/migrations/storage.js +241 -0
- package/dist/migrations/users.d.ts +11 -0
- package/dist/migrations/users.js +114 -0
- package/dist/migrations/validationRules.d.ts +43 -0
- package/dist/migrations/validationRules.js +42 -0
- package/dist/schemas/authUser.d.ts +62 -0
- package/dist/schemas/authUser.js +17 -0
- package/dist/setup.d.ts +2 -0
- package/dist/setup.js +5 -0
- package/dist/types.d.ts +9 -0
- package/dist/types.js +5 -0
- package/dist/utils/configSchema.json +742 -0
- package/dist/utils/helperFunctions.d.ts +34 -0
- package/dist/utils/helperFunctions.js +72 -0
- package/dist/utils/index.d.ts +2 -0
- package/dist/utils/index.js +2 -0
- package/dist/utils/setupFiles.d.ts +2 -0
- package/dist/utils/setupFiles.js +276 -0
- package/dist/utilsController.d.ts +30 -0
- package/dist/utilsController.js +106 -0
- package/package.json +34 -0
- package/src/main.ts +77 -0
- package/src/migrations/afterImportActions.ts +300 -0
- package/src/migrations/attributes.ts +315 -0
- package/src/migrations/backup.ts +189 -0
- package/src/migrations/collections.ts +303 -0
- package/src/migrations/converters.ts +628 -0
- package/src/migrations/dbHelpers.ts +89 -0
- package/src/migrations/importController.ts +509 -0
- package/src/migrations/importDataActions.ts +313 -0
- package/src/migrations/indexes.ts +37 -0
- package/src/migrations/logging.ts +15 -0
- package/src/migrations/migrationHelper.ts +100 -0
- package/src/migrations/queue.ts +119 -0
- package/src/migrations/relationships.ts +336 -0
- package/src/migrations/schema.ts +590 -0
- package/src/migrations/schemaStrings.ts +310 -0
- package/src/migrations/setupDatabase.ts +219 -0
- package/src/migrations/storage.ts +351 -0
- package/src/migrations/users.ts +148 -0
- package/src/migrations/validationRules.ts +63 -0
- package/src/schemas/authUser.ts +23 -0
- package/src/setup.ts +8 -0
- package/src/types.ts +14 -0
- package/src/utils/configSchema.json +742 -0
- package/src/utils/helperFunctions.ts +111 -0
- package/src/utils/index.ts +2 -0
- package/src/utils/setupFiles.ts +295 -0
- package/src/utilsController.ts +173 -0
- package/tsconfig.json +37 -0
|
@@ -0,0 +1,310 @@
|
|
|
1
|
+
import { toCamelCase, toPascalCase } from "../utils/index.js";
|
|
2
|
+
import type {
|
|
3
|
+
AppwriteConfig,
|
|
4
|
+
Attribute,
|
|
5
|
+
RelationshipAttribute,
|
|
6
|
+
} from "./schema.js";
|
|
7
|
+
import { z } from "zod";
|
|
8
|
+
import fs from "fs";
|
|
9
|
+
import path from "path";
|
|
10
|
+
|
|
11
|
+
interface RelationshipDetail {
|
|
12
|
+
parentCollection: string;
|
|
13
|
+
childCollection: string;
|
|
14
|
+
parentKey: string;
|
|
15
|
+
childKey: string;
|
|
16
|
+
isArray: boolean;
|
|
17
|
+
isChild: boolean;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export class SchemaGenerator {
|
|
21
|
+
private relationshipMap = new Map<string, RelationshipDetail[]>();
|
|
22
|
+
private config: AppwriteConfig;
|
|
23
|
+
private appwriteFolderPath: string;
|
|
24
|
+
|
|
25
|
+
constructor(config: AppwriteConfig, appwriteFolderPath: string) {
|
|
26
|
+
this.config = config;
|
|
27
|
+
this.appwriteFolderPath = appwriteFolderPath;
|
|
28
|
+
this.extractRelationships();
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
private extractRelationships(): void {
|
|
32
|
+
this.config.collections.forEach((collection) => {
|
|
33
|
+
collection.attributes.forEach((attr) => {
|
|
34
|
+
if (attr.type === "relationship" && attr.twoWay && attr.twoWayKey) {
|
|
35
|
+
const relationshipAttr = attr as RelationshipAttribute;
|
|
36
|
+
console.log(`Extracting relationship: ${attr.key}`);
|
|
37
|
+
let isArrayParent = false;
|
|
38
|
+
let isArrayChild = false;
|
|
39
|
+
switch (relationshipAttr.relationType) {
|
|
40
|
+
case "oneToMany":
|
|
41
|
+
isArrayParent = true;
|
|
42
|
+
isArrayChild = false;
|
|
43
|
+
break;
|
|
44
|
+
case "manyToMany":
|
|
45
|
+
isArrayParent = true;
|
|
46
|
+
isArrayChild = true;
|
|
47
|
+
break;
|
|
48
|
+
case "oneToOne":
|
|
49
|
+
isArrayParent = false;
|
|
50
|
+
isArrayChild = false;
|
|
51
|
+
break;
|
|
52
|
+
case "manyToOne":
|
|
53
|
+
isArrayParent = false;
|
|
54
|
+
isArrayChild = true;
|
|
55
|
+
break;
|
|
56
|
+
default:
|
|
57
|
+
break;
|
|
58
|
+
}
|
|
59
|
+
this.addRelationship(
|
|
60
|
+
collection.name,
|
|
61
|
+
relationshipAttr.relatedCollection,
|
|
62
|
+
attr.key,
|
|
63
|
+
relationshipAttr.twoWayKey,
|
|
64
|
+
isArrayParent,
|
|
65
|
+
isArrayChild
|
|
66
|
+
);
|
|
67
|
+
}
|
|
68
|
+
});
|
|
69
|
+
});
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
private addRelationship(
|
|
73
|
+
parentCollection: string,
|
|
74
|
+
childCollection: string,
|
|
75
|
+
parentKey: string,
|
|
76
|
+
childKey: string,
|
|
77
|
+
isArrayParent: boolean,
|
|
78
|
+
isArrayChild: boolean
|
|
79
|
+
): void {
|
|
80
|
+
const relationshipsChild = this.relationshipMap.get(childCollection) || [];
|
|
81
|
+
const relationshipsParent =
|
|
82
|
+
this.relationshipMap.get(parentCollection) || [];
|
|
83
|
+
relationshipsParent.push({
|
|
84
|
+
parentCollection,
|
|
85
|
+
childCollection,
|
|
86
|
+
parentKey,
|
|
87
|
+
childKey,
|
|
88
|
+
isArray: isArrayParent,
|
|
89
|
+
isChild: false,
|
|
90
|
+
});
|
|
91
|
+
relationshipsChild.push({
|
|
92
|
+
parentCollection,
|
|
93
|
+
childCollection,
|
|
94
|
+
parentKey,
|
|
95
|
+
childKey,
|
|
96
|
+
isArray: isArrayChild,
|
|
97
|
+
isChild: true,
|
|
98
|
+
});
|
|
99
|
+
this.relationshipMap.set(childCollection, relationshipsChild);
|
|
100
|
+
this.relationshipMap.set(parentCollection, relationshipsParent);
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
public generateSchemas(): void {
|
|
104
|
+
this.config.collections.forEach((collection) => {
|
|
105
|
+
const schemaString = this.createSchemaString(
|
|
106
|
+
collection.name,
|
|
107
|
+
collection.attributes
|
|
108
|
+
);
|
|
109
|
+
const camelCaseName = toCamelCase(collection.name);
|
|
110
|
+
const schemaPath = path.join(
|
|
111
|
+
this.appwriteFolderPath,
|
|
112
|
+
"schemas",
|
|
113
|
+
`${camelCaseName}.ts`
|
|
114
|
+
);
|
|
115
|
+
fs.writeFileSync(schemaPath, schemaString, { encoding: "utf-8" });
|
|
116
|
+
console.log(`Schema written to ${schemaPath}`);
|
|
117
|
+
});
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
createSchemaString = (name: string, attributes: Attribute[]): string => {
|
|
121
|
+
const pascalName = toPascalCase(name);
|
|
122
|
+
let imports = `import { z } from "zod";\n`;
|
|
123
|
+
|
|
124
|
+
// Use the relationshipMap to find related collections
|
|
125
|
+
const relationshipDetails = this.relationshipMap.get(name) || [];
|
|
126
|
+
const relatedCollections = relationshipDetails.map((detail) => {
|
|
127
|
+
const relatedCollectionName = detail.isChild
|
|
128
|
+
? detail.parentCollection
|
|
129
|
+
: detail.childCollection;
|
|
130
|
+
const key = detail.isChild ? detail.childKey : detail.parentKey;
|
|
131
|
+
const isArray = detail.isArray ? "array" : "";
|
|
132
|
+
return [relatedCollectionName, key, isArray];
|
|
133
|
+
});
|
|
134
|
+
|
|
135
|
+
console.log(relatedCollections);
|
|
136
|
+
|
|
137
|
+
let relatedTypes = "";
|
|
138
|
+
let relatedTypesLazy = "";
|
|
139
|
+
let curNum = 0;
|
|
140
|
+
let maxNum = relatedCollections.length;
|
|
141
|
+
relatedCollections.forEach((relatedCollection) => {
|
|
142
|
+
const relatedPascalName = toPascalCase(relatedCollection[0]);
|
|
143
|
+
const relatedCamelName = toCamelCase(relatedCollection[0]);
|
|
144
|
+
curNum++;
|
|
145
|
+
let endNameTypes = relatedPascalName;
|
|
146
|
+
let endNameLazy = `${relatedPascalName}Schema`;
|
|
147
|
+
if (relatedCollection[2] === "array") {
|
|
148
|
+
endNameTypes += "[]";
|
|
149
|
+
endNameLazy += ".array()";
|
|
150
|
+
}
|
|
151
|
+
endNameLazy += ".nullish()";
|
|
152
|
+
imports += `import { ${relatedPascalName}Schema, type ${relatedPascalName} } from "./${relatedCamelName}";\n`;
|
|
153
|
+
relatedTypes += `${relatedCollection[1]}?: ${endNameTypes};\n`;
|
|
154
|
+
if (relatedTypes.length > 0 && curNum !== maxNum) {
|
|
155
|
+
relatedTypes += " ";
|
|
156
|
+
}
|
|
157
|
+
relatedTypesLazy += `${relatedCollection[1]}: z.lazy(() => ${endNameLazy}),\n`;
|
|
158
|
+
if (relatedTypesLazy.length > 0 && curNum !== maxNum) {
|
|
159
|
+
relatedTypesLazy += " ";
|
|
160
|
+
}
|
|
161
|
+
});
|
|
162
|
+
|
|
163
|
+
let schemaString = `${imports}\n\n`;
|
|
164
|
+
schemaString += `export const ${pascalName}SchemaBase = z.object({\n`;
|
|
165
|
+
schemaString += ` $id: z.string().optional(),\n`;
|
|
166
|
+
schemaString += ` $createdAt: z.date().or(z.string()).optional(),\n`;
|
|
167
|
+
schemaString += ` $updatedAt: z.date().or(z.string()).optional(),\n`;
|
|
168
|
+
for (const attribute of attributes) {
|
|
169
|
+
if (attribute.type === "relationship") {
|
|
170
|
+
continue;
|
|
171
|
+
}
|
|
172
|
+
schemaString += ` ${attribute.key}: ${this.typeToZod(attribute)},\n`;
|
|
173
|
+
}
|
|
174
|
+
schemaString += `});\n\n`;
|
|
175
|
+
schemaString += `export type ${pascalName}Base = z.infer<typeof ${pascalName}SchemaBase>`;
|
|
176
|
+
if (relatedTypes.length > 0) {
|
|
177
|
+
schemaString += ` & {\n ${relatedTypes}};\n\n`;
|
|
178
|
+
} else {
|
|
179
|
+
schemaString += `;\n\n`;
|
|
180
|
+
}
|
|
181
|
+
schemaString += `export const ${pascalName}Schema: z.ZodType<${pascalName}Base> = ${pascalName}SchemaBase`;
|
|
182
|
+
if (relatedTypes.length > 0) {
|
|
183
|
+
schemaString += `.extend({\n ${relatedTypesLazy}});\n\n`;
|
|
184
|
+
} else {
|
|
185
|
+
schemaString += `;\n`;
|
|
186
|
+
}
|
|
187
|
+
schemaString += `export type ${pascalName} = z.infer<typeof ${pascalName}Schema>;\n\n`;
|
|
188
|
+
|
|
189
|
+
return schemaString;
|
|
190
|
+
};
|
|
191
|
+
|
|
192
|
+
typeToZod = (attribute: Attribute) => {
|
|
193
|
+
let baseSchemaCode = "";
|
|
194
|
+
|
|
195
|
+
switch (attribute.type) {
|
|
196
|
+
case "string":
|
|
197
|
+
baseSchemaCode = "z.string()";
|
|
198
|
+
if (attribute.size) {
|
|
199
|
+
baseSchemaCode += `.max(${attribute.size}, "Maximum length of ${attribute.size} characters exceeded")`;
|
|
200
|
+
}
|
|
201
|
+
if (attribute.xdefault !== undefined) {
|
|
202
|
+
baseSchemaCode += `.default("${attribute.xdefault}")`;
|
|
203
|
+
}
|
|
204
|
+
if (!attribute.required && !attribute.array) {
|
|
205
|
+
baseSchemaCode += ".nullish()";
|
|
206
|
+
}
|
|
207
|
+
break;
|
|
208
|
+
case "integer":
|
|
209
|
+
baseSchemaCode = "z.number().int()";
|
|
210
|
+
if (attribute.min !== undefined) {
|
|
211
|
+
baseSchemaCode += `.min(${attribute.min}, "Minimum value of ${attribute.min} not met")`;
|
|
212
|
+
}
|
|
213
|
+
if (attribute.max !== undefined) {
|
|
214
|
+
baseSchemaCode += `.max(${attribute.max}, "Maximum value of ${attribute.max} exceeded")`;
|
|
215
|
+
}
|
|
216
|
+
if (attribute.xdefault !== undefined) {
|
|
217
|
+
baseSchemaCode += `.default(${attribute.xdefault})`;
|
|
218
|
+
}
|
|
219
|
+
if (!attribute.required && !attribute.array) {
|
|
220
|
+
baseSchemaCode += ".nullish()";
|
|
221
|
+
}
|
|
222
|
+
break;
|
|
223
|
+
case "float":
|
|
224
|
+
baseSchemaCode = "z.number()";
|
|
225
|
+
if (attribute.min !== undefined) {
|
|
226
|
+
baseSchemaCode += `.min(${attribute.min}, "Minimum value of ${attribute.min} not met")`;
|
|
227
|
+
}
|
|
228
|
+
if (attribute.max !== undefined) {
|
|
229
|
+
baseSchemaCode += `.max(${attribute.max}, "Maximum value of ${attribute.max} exceeded")`;
|
|
230
|
+
}
|
|
231
|
+
if (attribute.xdefault !== undefined) {
|
|
232
|
+
baseSchemaCode += `.default(${attribute.xdefault})`;
|
|
233
|
+
}
|
|
234
|
+
if (!attribute.required && !attribute.array) {
|
|
235
|
+
baseSchemaCode += ".nullish()";
|
|
236
|
+
}
|
|
237
|
+
break;
|
|
238
|
+
case "boolean":
|
|
239
|
+
baseSchemaCode = "z.boolean()";
|
|
240
|
+
if (attribute.xdefault !== undefined) {
|
|
241
|
+
baseSchemaCode += `.default(${attribute.xdefault})`;
|
|
242
|
+
}
|
|
243
|
+
if (!attribute.required && !attribute.array) {
|
|
244
|
+
baseSchemaCode += ".nullish()";
|
|
245
|
+
}
|
|
246
|
+
break;
|
|
247
|
+
case "datetime":
|
|
248
|
+
baseSchemaCode = "z.date()";
|
|
249
|
+
if (attribute.xdefault !== undefined) {
|
|
250
|
+
baseSchemaCode += `.default(new Date("${attribute.xdefault}"))`;
|
|
251
|
+
}
|
|
252
|
+
if (!attribute.required && !attribute.array) {
|
|
253
|
+
baseSchemaCode += ".nullish()";
|
|
254
|
+
}
|
|
255
|
+
break;
|
|
256
|
+
case "email":
|
|
257
|
+
baseSchemaCode = "z.string().email()";
|
|
258
|
+
if (attribute.xdefault !== undefined) {
|
|
259
|
+
baseSchemaCode += `.default("${attribute.xdefault}")`;
|
|
260
|
+
}
|
|
261
|
+
if (!attribute.required && !attribute.array) {
|
|
262
|
+
baseSchemaCode += ".nullish()";
|
|
263
|
+
}
|
|
264
|
+
break;
|
|
265
|
+
case "ip":
|
|
266
|
+
baseSchemaCode = "z.string()"; // Add custom validation as needed
|
|
267
|
+
if (attribute.xdefault !== undefined) {
|
|
268
|
+
baseSchemaCode += `.default("${attribute.xdefault}")`;
|
|
269
|
+
}
|
|
270
|
+
if (!attribute.required && !attribute.array) {
|
|
271
|
+
baseSchemaCode += ".nullish()";
|
|
272
|
+
}
|
|
273
|
+
break;
|
|
274
|
+
case "url":
|
|
275
|
+
baseSchemaCode = "z.string().url()";
|
|
276
|
+
if (attribute.xdefault !== undefined) {
|
|
277
|
+
baseSchemaCode += `.default("${attribute.xdefault}")`;
|
|
278
|
+
}
|
|
279
|
+
if (!attribute.required && !attribute.array) {
|
|
280
|
+
baseSchemaCode += ".nullish()";
|
|
281
|
+
}
|
|
282
|
+
break;
|
|
283
|
+
case "enum":
|
|
284
|
+
baseSchemaCode = `z.enum([${attribute.elements
|
|
285
|
+
.map((element) => `"${element}"`)
|
|
286
|
+
.join(", ")}])`;
|
|
287
|
+
if (attribute.xdefault !== undefined) {
|
|
288
|
+
baseSchemaCode += `.default("${attribute.xdefault}")`;
|
|
289
|
+
}
|
|
290
|
+
if (!attribute.required && !attribute.array) {
|
|
291
|
+
baseSchemaCode += ".nullish()";
|
|
292
|
+
}
|
|
293
|
+
break;
|
|
294
|
+
case "relationship":
|
|
295
|
+
break;
|
|
296
|
+
default:
|
|
297
|
+
baseSchemaCode = "z.any()";
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
// Handle arrays
|
|
301
|
+
if (attribute.array) {
|
|
302
|
+
baseSchemaCode = `z.array(${baseSchemaCode})`;
|
|
303
|
+
}
|
|
304
|
+
if (attribute.array && !attribute.required) {
|
|
305
|
+
baseSchemaCode += ".nullish()";
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
return baseSchemaCode;
|
|
309
|
+
};
|
|
310
|
+
}
|
|
@@ -0,0 +1,219 @@
|
|
|
1
|
+
import { Databases, ID, Query, Storage, type Models } from "node-appwrite";
|
|
2
|
+
import { createOrUpdateAttribute } from "./attributes.js";
|
|
3
|
+
import {
|
|
4
|
+
createOrUpdateCollections,
|
|
5
|
+
generateSchemas,
|
|
6
|
+
wipeDatabase,
|
|
7
|
+
} from "./collections.js";
|
|
8
|
+
import { getMigrationCollectionSchemas } from "./backup.js";
|
|
9
|
+
import { areCollectionNamesSame, toCamelCase } from "../utils/index.js";
|
|
10
|
+
import {
|
|
11
|
+
backupDatabase,
|
|
12
|
+
initOrGetBackupStorage,
|
|
13
|
+
initOrGetDocumentStorage,
|
|
14
|
+
wipeDocumentStorage,
|
|
15
|
+
} from "./storage.js";
|
|
16
|
+
import { type AppwriteConfig } from "./schema.js";
|
|
17
|
+
import type { SetupOptions } from "../utilsController.js";
|
|
18
|
+
import { nameToIdMapping } from "./queue.js";
|
|
19
|
+
import { UsersController } from "./users.js";
|
|
20
|
+
|
|
21
|
+
export const setupMigrationDatabase = async (config: AppwriteConfig) => {
|
|
22
|
+
// Create the migrations database if needed
|
|
23
|
+
console.log("---------------------------------");
|
|
24
|
+
console.log("Starting Migrations Setup");
|
|
25
|
+
console.log("---------------------------------");
|
|
26
|
+
const database = new Databases(config.appwriteClient!);
|
|
27
|
+
let db: Models.Database | null = null;
|
|
28
|
+
const dbCollections: Models.Collection[] = [];
|
|
29
|
+
const migrationCollectionsSetup = getMigrationCollectionSchemas();
|
|
30
|
+
try {
|
|
31
|
+
db = await database.get("migrations");
|
|
32
|
+
console.log("Migrations database found");
|
|
33
|
+
} catch (e) {
|
|
34
|
+
db = await database.create("migrations", "Migrations", true);
|
|
35
|
+
console.log("Migrations database created");
|
|
36
|
+
}
|
|
37
|
+
if (db) {
|
|
38
|
+
const collectionsPulled = await database.listCollections(db.$id, [
|
|
39
|
+
Query.limit(25),
|
|
40
|
+
]);
|
|
41
|
+
dbCollections.push(...collectionsPulled.collections);
|
|
42
|
+
}
|
|
43
|
+
console.log(`Collections in migrations database: ${dbCollections.length}`);
|
|
44
|
+
|
|
45
|
+
// Iterate over each key in the migrationCollectionsSetup object
|
|
46
|
+
for (const [collectionName, { collection, attributes }] of Object.entries(
|
|
47
|
+
migrationCollectionsSetup
|
|
48
|
+
)) {
|
|
49
|
+
const collectionId = toCamelCase(collectionName); // Convert name to toCamelCase for the ID
|
|
50
|
+
let collectionFound: Models.Collection | null = null;
|
|
51
|
+
try {
|
|
52
|
+
collectionFound = await database.getCollection(db.$id, collectionId);
|
|
53
|
+
} catch (e) {
|
|
54
|
+
console.log(`Collection not found: ${collectionId}`);
|
|
55
|
+
}
|
|
56
|
+
if (!collectionFound) {
|
|
57
|
+
// Create the collection with the provided configuration
|
|
58
|
+
collectionFound = await database.createCollection(
|
|
59
|
+
db.$id,
|
|
60
|
+
collectionId,
|
|
61
|
+
collectionName,
|
|
62
|
+
undefined,
|
|
63
|
+
collection.documentSecurity,
|
|
64
|
+
collection.enabled
|
|
65
|
+
);
|
|
66
|
+
}
|
|
67
|
+
for (const attribute of attributes) {
|
|
68
|
+
await createOrUpdateAttribute(
|
|
69
|
+
database,
|
|
70
|
+
db.$id,
|
|
71
|
+
collectionFound,
|
|
72
|
+
attribute
|
|
73
|
+
);
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
console.log("---------------------------------");
|
|
77
|
+
console.log("Migrations Setup Complete");
|
|
78
|
+
console.log("---------------------------------");
|
|
79
|
+
};
|
|
80
|
+
|
|
81
|
+
export const ensureDatabasesExist = async (config: AppwriteConfig) => {
|
|
82
|
+
const database = new Databases(config.appwriteClient);
|
|
83
|
+
const databasesToEnsure = config.databases;
|
|
84
|
+
databasesToEnsure.push({
|
|
85
|
+
$id: "migrations",
|
|
86
|
+
name: "Migrations",
|
|
87
|
+
});
|
|
88
|
+
const dbNames = databasesToEnsure.map((db) => db.name);
|
|
89
|
+
|
|
90
|
+
const existingDatabases = await database.list([Query.equal("name", dbNames)]);
|
|
91
|
+
|
|
92
|
+
for (const db of databasesToEnsure) {
|
|
93
|
+
if (!existingDatabases.databases.some((d) => d.name === db.name)) {
|
|
94
|
+
await database.create(db.$id || ID.unique(), db.name, true);
|
|
95
|
+
console.log(`${db.name} database created`);
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
};
|
|
99
|
+
|
|
100
|
+
export const wipeOtherDatabases = async (
|
|
101
|
+
database: Databases,
|
|
102
|
+
config: AppwriteConfig
|
|
103
|
+
) => {
|
|
104
|
+
const databasesToKeep = config.databases.map((db) =>
|
|
105
|
+
db.name.toLowerCase().trim().replace(" ", "")
|
|
106
|
+
);
|
|
107
|
+
databasesToKeep.push("migrations");
|
|
108
|
+
console.log(`Databases to keep: ${databasesToKeep.join(", ")}`);
|
|
109
|
+
const allDatabases = await database.list([Query.limit(500)]);
|
|
110
|
+
for (const db of allDatabases.databases) {
|
|
111
|
+
if (
|
|
112
|
+
!databasesToKeep.includes(db.name.toLowerCase().trim().replace(" ", ""))
|
|
113
|
+
) {
|
|
114
|
+
await database.delete(db.$id);
|
|
115
|
+
console.log(`Deleted database: ${db.name}`);
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
};
|
|
119
|
+
|
|
120
|
+
export const startSetup = async (
|
|
121
|
+
database: Databases,
|
|
122
|
+
storage: Storage,
|
|
123
|
+
config: AppwriteConfig,
|
|
124
|
+
setupOptions: SetupOptions,
|
|
125
|
+
appwriteFolderPath: string
|
|
126
|
+
) => {
|
|
127
|
+
await setupMigrationDatabase(config);
|
|
128
|
+
|
|
129
|
+
if (config.enableBackups) {
|
|
130
|
+
await initOrGetBackupStorage(storage);
|
|
131
|
+
if (setupOptions.wipeDocumentStorage) {
|
|
132
|
+
if (setupOptions.runProd) {
|
|
133
|
+
await initOrGetDocumentStorage(
|
|
134
|
+
storage,
|
|
135
|
+
config,
|
|
136
|
+
config.databases[0].name
|
|
137
|
+
);
|
|
138
|
+
await wipeDocumentStorage(storage, config, config.databases[0].name);
|
|
139
|
+
}
|
|
140
|
+
if (setupOptions.runStaging) {
|
|
141
|
+
await initOrGetDocumentStorage(
|
|
142
|
+
storage,
|
|
143
|
+
config,
|
|
144
|
+
config.databases[1].name
|
|
145
|
+
);
|
|
146
|
+
await wipeDocumentStorage(storage, config, config.databases[1].name);
|
|
147
|
+
}
|
|
148
|
+
if (setupOptions.runDev) {
|
|
149
|
+
await initOrGetDocumentStorage(
|
|
150
|
+
storage,
|
|
151
|
+
config,
|
|
152
|
+
config.databases[2].name
|
|
153
|
+
);
|
|
154
|
+
await wipeDocumentStorage(storage, config, config.databases[2].name);
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
if (config.enableWipeOtherDatabases) {
|
|
159
|
+
await wipeOtherDatabases(database, config);
|
|
160
|
+
}
|
|
161
|
+
if (setupOptions.wipeUsers) {
|
|
162
|
+
const usersController = new UsersController(config, database);
|
|
163
|
+
console.log("Wiping users");
|
|
164
|
+
await usersController.wipeUsers();
|
|
165
|
+
console.log("Users wiped");
|
|
166
|
+
}
|
|
167
|
+
await ensureDatabasesExist(config);
|
|
168
|
+
|
|
169
|
+
const databaseNames = config.databases.map((db) => db.name);
|
|
170
|
+
|
|
171
|
+
// Move to here so it always runs if it's set to true
|
|
172
|
+
if (setupOptions.generateSchemas) {
|
|
173
|
+
await generateSchemas(config, appwriteFolderPath);
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
for (const db of config.databases) {
|
|
177
|
+
// Determine if the current database should be processed based on the setup options
|
|
178
|
+
const processDatabase =
|
|
179
|
+
(setupOptions.runProd &&
|
|
180
|
+
areCollectionNamesSame(db.name, databaseNames[0])) ||
|
|
181
|
+
(setupOptions.runStaging &&
|
|
182
|
+
areCollectionNamesSame(db.name, databaseNames[1])) ||
|
|
183
|
+
(setupOptions.runDev &&
|
|
184
|
+
areCollectionNamesSame(db.name, databaseNames[2]));
|
|
185
|
+
if (!processDatabase) {
|
|
186
|
+
continue;
|
|
187
|
+
} else {
|
|
188
|
+
await initOrGetDocumentStorage(storage, config, db.name);
|
|
189
|
+
}
|
|
190
|
+
console.log(`---------------------------------`);
|
|
191
|
+
console.log(`Starting setup for database: ${db.name}`);
|
|
192
|
+
console.log(`---------------------------------`);
|
|
193
|
+
let deletedCollections:
|
|
194
|
+
| { collectionId: string; collectionName: string }[]
|
|
195
|
+
| undefined;
|
|
196
|
+
|
|
197
|
+
if (setupOptions.wipeDatabases && processDatabase) {
|
|
198
|
+
if (config.enableBackups && setupOptions.doBackup) {
|
|
199
|
+
await backupDatabase(database, db.$id, storage);
|
|
200
|
+
}
|
|
201
|
+
deletedCollections = await wipeDatabase(database, db.$id);
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
if (processDatabase) {
|
|
205
|
+
await createOrUpdateCollections(
|
|
206
|
+
database,
|
|
207
|
+
db.$id,
|
|
208
|
+
config,
|
|
209
|
+
deletedCollections
|
|
210
|
+
);
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
deletedCollections = undefined;
|
|
214
|
+
nameToIdMapping.clear();
|
|
215
|
+
console.log(`---------------------------------`);
|
|
216
|
+
console.log(`Finished setup for database: ${db.name}`);
|
|
217
|
+
console.log(`---------------------------------`);
|
|
218
|
+
}
|
|
219
|
+
};
|