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.
Files changed (86) hide show
  1. package/README.md +80 -0
  2. package/dist/main.d.ts +2 -0
  3. package/dist/main.js +74 -0
  4. package/dist/migrations/afterImportActions.d.ts +12 -0
  5. package/dist/migrations/afterImportActions.js +196 -0
  6. package/dist/migrations/attributes.d.ts +4 -0
  7. package/dist/migrations/attributes.js +158 -0
  8. package/dist/migrations/backup.d.ts +621 -0
  9. package/dist/migrations/backup.js +159 -0
  10. package/dist/migrations/collections.d.ts +16 -0
  11. package/dist/migrations/collections.js +207 -0
  12. package/dist/migrations/converters.d.ts +179 -0
  13. package/dist/migrations/converters.js +575 -0
  14. package/dist/migrations/dbHelpers.d.ts +5 -0
  15. package/dist/migrations/dbHelpers.js +54 -0
  16. package/dist/migrations/importController.d.ts +44 -0
  17. package/dist/migrations/importController.js +312 -0
  18. package/dist/migrations/importDataActions.d.ts +44 -0
  19. package/dist/migrations/importDataActions.js +219 -0
  20. package/dist/migrations/indexes.d.ts +4 -0
  21. package/dist/migrations/indexes.js +18 -0
  22. package/dist/migrations/logging.d.ts +2 -0
  23. package/dist/migrations/logging.js +14 -0
  24. package/dist/migrations/migrationHelper.d.ts +18 -0
  25. package/dist/migrations/migrationHelper.js +66 -0
  26. package/dist/migrations/queue.d.ts +13 -0
  27. package/dist/migrations/queue.js +79 -0
  28. package/dist/migrations/relationships.d.ts +90 -0
  29. package/dist/migrations/relationships.js +209 -0
  30. package/dist/migrations/schema.d.ts +3142 -0
  31. package/dist/migrations/schema.js +485 -0
  32. package/dist/migrations/schemaStrings.d.ts +12 -0
  33. package/dist/migrations/schemaStrings.js +261 -0
  34. package/dist/migrations/setupDatabase.d.ts +7 -0
  35. package/dist/migrations/setupDatabase.js +151 -0
  36. package/dist/migrations/storage.d.ts +8 -0
  37. package/dist/migrations/storage.js +241 -0
  38. package/dist/migrations/users.d.ts +11 -0
  39. package/dist/migrations/users.js +114 -0
  40. package/dist/migrations/validationRules.d.ts +43 -0
  41. package/dist/migrations/validationRules.js +42 -0
  42. package/dist/schemas/authUser.d.ts +62 -0
  43. package/dist/schemas/authUser.js +17 -0
  44. package/dist/setup.d.ts +2 -0
  45. package/dist/setup.js +5 -0
  46. package/dist/types.d.ts +9 -0
  47. package/dist/types.js +5 -0
  48. package/dist/utils/configSchema.json +742 -0
  49. package/dist/utils/helperFunctions.d.ts +34 -0
  50. package/dist/utils/helperFunctions.js +72 -0
  51. package/dist/utils/index.d.ts +2 -0
  52. package/dist/utils/index.js +2 -0
  53. package/dist/utils/setupFiles.d.ts +2 -0
  54. package/dist/utils/setupFiles.js +276 -0
  55. package/dist/utilsController.d.ts +30 -0
  56. package/dist/utilsController.js +106 -0
  57. package/package.json +34 -0
  58. package/src/main.ts +77 -0
  59. package/src/migrations/afterImportActions.ts +300 -0
  60. package/src/migrations/attributes.ts +315 -0
  61. package/src/migrations/backup.ts +189 -0
  62. package/src/migrations/collections.ts +303 -0
  63. package/src/migrations/converters.ts +628 -0
  64. package/src/migrations/dbHelpers.ts +89 -0
  65. package/src/migrations/importController.ts +509 -0
  66. package/src/migrations/importDataActions.ts +313 -0
  67. package/src/migrations/indexes.ts +37 -0
  68. package/src/migrations/logging.ts +15 -0
  69. package/src/migrations/migrationHelper.ts +100 -0
  70. package/src/migrations/queue.ts +119 -0
  71. package/src/migrations/relationships.ts +336 -0
  72. package/src/migrations/schema.ts +590 -0
  73. package/src/migrations/schemaStrings.ts +310 -0
  74. package/src/migrations/setupDatabase.ts +219 -0
  75. package/src/migrations/storage.ts +351 -0
  76. package/src/migrations/users.ts +148 -0
  77. package/src/migrations/validationRules.ts +63 -0
  78. package/src/schemas/authUser.ts +23 -0
  79. package/src/setup.ts +8 -0
  80. package/src/types.ts +14 -0
  81. package/src/utils/configSchema.json +742 -0
  82. package/src/utils/helperFunctions.ts +111 -0
  83. package/src/utils/index.ts +2 -0
  84. package/src/utils/setupFiles.ts +295 -0
  85. package/src/utilsController.ts +173 -0
  86. package/tsconfig.json +37 -0
@@ -0,0 +1,34 @@
1
+ import type { Models } from "node-appwrite";
2
+ export declare const toPascalCase: (str: string) => string;
3
+ export declare const toCamelCase: (str: string) => string;
4
+ export declare const ensureDirectoryExistence: (filePath: string) => true | undefined;
5
+ export declare const writeFileSync: (filePath: string, content: string, options: {
6
+ flag: string;
7
+ }) => void;
8
+ export declare const readFileSync: (filePath: string) => string;
9
+ export declare const existsSync: (filePath: string) => boolean;
10
+ export declare const mkdirSync: (filePath: string) => void;
11
+ export declare const readdirSync: (filePath: string) => string[];
12
+ export declare const areCollectionNamesSame: (a: string, b: string) => boolean;
13
+ /**
14
+ * Generates the view URL for a specific file based on the provided endpoint, project ID, bucket ID, file ID, and optional JWT token.
15
+ *
16
+ * @param {string} endpoint - the base URL endpoint
17
+ * @param {string} projectId - the ID of the project
18
+ * @param {string} bucketId - the ID of the bucket
19
+ * @param {string} fileId - the ID of the file
20
+ * @param {Models.Jwt} [jwt] - optional JWT token generated via the Appwrite SDK
21
+ * @return {string} the generated view URL for the file
22
+ */
23
+ export declare const getFileViewUrl: (endpoint: string, projectId: string, bucketId: string, fileId: string, jwt?: Models.Jwt) => string;
24
+ /**
25
+ * Generates a download URL for a file based on the provided endpoint, project ID, bucket ID, file ID, and optionally a JWT.
26
+ *
27
+ * @param {string} endpoint - The base URL endpoint.
28
+ * @param {string} projectId - The ID of the project.
29
+ * @param {string} bucketId - The ID of the bucket.
30
+ * @param {string} fileId - The ID of the file.
31
+ * @param {Models.Jwt} [jwt] - Optional JWT object for authentication with Appwrite.
32
+ * @return {string} The complete download URL for the file.
33
+ */
34
+ export declare const getFileDownloadUrl: (endpoint: string, projectId: string, bucketId: string, fileId: string, jwt?: Models.Jwt) => string;
@@ -0,0 +1,72 @@
1
+ import fs from "node:fs";
2
+ import path from "node:path";
3
+ export const toPascalCase = (str) => {
4
+ return (str
5
+ // Split the string into words on spaces or camelCase transitions
6
+ .split(/(?:\s+)|(?:([A-Z][a-z]+))/g)
7
+ // Filter out empty strings that can appear due to the split regex
8
+ .filter(Boolean)
9
+ // Capitalize the first letter of each word and join them together
10
+ .map((word) => word.charAt(0).toUpperCase() + word.slice(1))
11
+ .join(""));
12
+ };
13
+ export const toCamelCase = (str) => {
14
+ return str
15
+ .replace(/(?:^\w|[A-Z]|\b\w)/g, (word, index) => index === 0 ? word.toLowerCase() : word.toUpperCase())
16
+ .replace(/\s+/g, "");
17
+ };
18
+ export const ensureDirectoryExistence = (filePath) => {
19
+ const dirname = path.dirname(filePath);
20
+ if (fs.existsSync(dirname)) {
21
+ return true;
22
+ }
23
+ ensureDirectoryExistence(dirname);
24
+ fs.mkdirSync(dirname);
25
+ };
26
+ export const writeFileSync = (filePath, content, options) => {
27
+ ensureDirectoryExistence(filePath);
28
+ fs.writeFileSync(filePath, content, options);
29
+ };
30
+ export const readFileSync = (filePath) => {
31
+ return fs.readFileSync(filePath, "utf8");
32
+ };
33
+ export const existsSync = (filePath) => {
34
+ return fs.existsSync(filePath);
35
+ };
36
+ export const mkdirSync = (filePath) => {
37
+ ensureDirectoryExistence(filePath);
38
+ fs.mkdirSync(filePath);
39
+ };
40
+ export const readdirSync = (filePath) => {
41
+ return fs.readdirSync(filePath);
42
+ };
43
+ export const areCollectionNamesSame = (a, b) => {
44
+ return (a.toLowerCase().trim().replace(" ", "") ===
45
+ b.toLowerCase().trim().replace(" ", ""));
46
+ };
47
+ /**
48
+ * Generates the view URL for a specific file based on the provided endpoint, project ID, bucket ID, file ID, and optional JWT token.
49
+ *
50
+ * @param {string} endpoint - the base URL endpoint
51
+ * @param {string} projectId - the ID of the project
52
+ * @param {string} bucketId - the ID of the bucket
53
+ * @param {string} fileId - the ID of the file
54
+ * @param {Models.Jwt} [jwt] - optional JWT token generated via the Appwrite SDK
55
+ * @return {string} the generated view URL for the file
56
+ */
57
+ export const getFileViewUrl = (endpoint, projectId, bucketId, fileId, jwt) => {
58
+ return `${endpoint}/storage/buckets/${bucketId}/files/${fileId}/view?project=${projectId}${jwt ? `&jwt=${jwt.jwt}` : ""}`;
59
+ };
60
+ /**
61
+ * Generates a download URL for a file based on the provided endpoint, project ID, bucket ID, file ID, and optionally a JWT.
62
+ *
63
+ * @param {string} endpoint - The base URL endpoint.
64
+ * @param {string} projectId - The ID of the project.
65
+ * @param {string} bucketId - The ID of the bucket.
66
+ * @param {string} fileId - The ID of the file.
67
+ * @param {Models.Jwt} [jwt] - Optional JWT object for authentication with Appwrite.
68
+ * @return {string} The complete download URL for the file.
69
+ */
70
+ export const getFileDownloadUrl = (endpoint, projectId, bucketId, fileId, jwt) => {
71
+ return `${endpoint}/storage/buckets/${bucketId}/files/${fileId}/download?project=${projectId}${jwt ? `&jwt=${jwt.jwt}` : ""}`;
72
+ };
@@ -0,0 +1,2 @@
1
+ export * from "./helperFunctions.js";
2
+ export * from "./setupFiles.js";
@@ -0,0 +1,2 @@
1
+ export * from "./helperFunctions.js";
2
+ export * from "./setupFiles.js";
@@ -0,0 +1,2 @@
1
+ export declare const customDefinitionsFile = "import type { ConverterFunctions, ValidationRules, AfterImportActions } from \"appwrite-utils\";\n\nexport const customConverterFunctions: ConverterFunctions = {\n // Add your custom converter functions here\n}\nexport const customValidationRules: ValidationRules = {\n // Add your custom validation rules here\n}\nexport const customAfterImportActions: AfterImportActions = {\n // Add your custom after import actions here\n}";
2
+ export declare const setupDirsFiles: (example?: boolean) => Promise<void>;
@@ -0,0 +1,276 @@
1
+ import { mkdirSync, writeFileSync, existsSync } from "node:fs";
2
+ import path from "node:path";
3
+ import configSchema from "./configSchema.json" assert { type: "json" };
4
+ // Define our YAML files
5
+ // Define our YAML files
6
+ const configFileExample = `# yaml-language-server: $schema=./.appwrite/appwriteUtilsConfigSchema.json
7
+ # Appwrite configuration settings
8
+ appwriteEndpoint: 'https://cloud.appwrite.io/v1' # Your Appwrite endpoint. Default: 'https://cloud.appwrite.io/v1'
9
+ appwriteProject: 'YOUR_PROJECT_ID' # Your Appwrite project ID
10
+ appwriteKey: 'YOUR_API_KEY' # Your Appwrite API key (needs storage and databases at minimum)
11
+ appwriteClient: null # Your Appwrite client -- don't worry about this
12
+ enableDevDatabase: true # Enable development database alongside main
13
+ enableBackups: true # Enable backups
14
+ backupInterval: 3600 # Backup interval in seconds
15
+ backupRetention: 30 # Backup retention in days
16
+ enableBackupCleanup: true # Enable backup cleanup
17
+ enableMockData: false # Enable mock data generation
18
+ enableWipeOtherDatabases: true # Enable wiping other databases
19
+ documentBucketId: 'documents' # Your Appwrite bucket ID for documents
20
+ usersCollectionName: 'Members' # Your Appwrite collection for any extra info while importing members (if any)
21
+ # This allows you to use any targetKey in the users field to create your user
22
+ # these are: name, email, phone, labels, prefs, password, userId and (not yet added)
23
+ # $createdAt, $updatedAt -- Add them to your attributeMappings (NOT attributes) to define them and set the
24
+ # targetKey to the same as the Appwrite targetKey
25
+ databases:
26
+ - $id: 'main'
27
+ name: 'Main'
28
+ - $id: 'staging'
29
+ name: 'Staging'
30
+ - $id: 'dev'
31
+ name: 'Development'
32
+ collections:
33
+ - name: 'Members'
34
+ $permissions:
35
+ - permission: read
36
+ target: any
37
+ - permission: create
38
+ target: users
39
+ - permission: update
40
+ target: users
41
+ - permission: delete
42
+ target: users
43
+ attributes:
44
+ - key: 'idOrig'
45
+ type: 'string'
46
+ size: 255
47
+ required: false
48
+ - key: 'dogs'
49
+ type: 'relationship'
50
+ relatedCollection: 'Dogs'
51
+ relationType: 'oneToMany'
52
+ twoWay: true
53
+ twoWayKey: 'owner'
54
+ side: 'parent'
55
+ onDelete: 'cascade'
56
+ importMapping: { originalIdField: 'idOrig', targetField: 'ownerIdOrig' }
57
+ - key: 'dogIds'
58
+ type: 'string'
59
+ size: 255
60
+ array: true
61
+ - key: 'profilePhoto'
62
+ type: 'string'
63
+ size: 255
64
+ required: false
65
+ - key: 'profilePhotoTest'
66
+ type: 'string'
67
+ size: 255
68
+ required: false
69
+ indexes:
70
+ - key: 'idOrig_index'
71
+ type: 'key'
72
+ attributes: ['idOrig']
73
+ importDefs:
74
+ - filePath: 'importData/members.json'
75
+ basePath: 'RECORDS'
76
+ attributeMappings:
77
+ - oldKey: 'id'
78
+ targetKey: 'idOrig'
79
+ converters: ['anyToString']
80
+ postImportActions:
81
+ - action: 'checkAndUpdateFieldInDocument'
82
+ params:
83
+ - "{dbId}"
84
+ - "{collId}"
85
+ - "{docId}"
86
+ - "idOrig"
87
+ - "{id}"
88
+ - "{$id}"
89
+ - oldKey: 'name'
90
+ targetKey: 'name'
91
+ - oldKey: 'email'
92
+ targetKey: 'email'
93
+ - oldKey: 'doesntMatter'
94
+ targetKey: 'profilePhoto'
95
+ fileData: { name: "profilePhoto_{id}", path: "importData/profilePhotos" }
96
+ - oldKey: 'photoUrl'
97
+ targetKey: 'profilePhotoTest'
98
+ fileData: { name: "profilePhotoTest_{id}", path: "{photoUrl}" }
99
+ - name: 'Dogs'
100
+ $permissions:
101
+ - permission: read
102
+ target: any
103
+ - permission: create
104
+ target: users
105
+ - permission: update
106
+ target: users
107
+ - permission: delete
108
+ target: users
109
+ attributes:
110
+ - key: 'name'
111
+ type: 'string'
112
+ size: 255
113
+ required: true
114
+ - key: 'breed'
115
+ type: 'string'
116
+ size: 255
117
+ required: false
118
+ - key: 'age'
119
+ type: 'integer'
120
+ required: false
121
+ min: 0
122
+ max: 100
123
+ - key: 'idOrig'
124
+ type: 'string'
125
+ size: 20
126
+ required: false
127
+ - key: 'ownerIdOrig'
128
+ type: 'string'
129
+ size: 255
130
+ required: false
131
+ - key: 'vetRecords'
132
+ type: 'string'
133
+ size: 255
134
+ required: false
135
+ - key: 'vetRecordIds'
136
+ type: 'string'
137
+ size: 255
138
+ array: true
139
+ required: false
140
+ indexes:
141
+ - key: 'ownerIdIndex'
142
+ type: 'key'
143
+ attributes: ['ownerIdOrig']
144
+ importDefs:
145
+ - filePath: 'importData/dogs.json'
146
+ basePath: 'RECORDS'
147
+ attributeMappings:
148
+ - oldKey: 'id'
149
+ targetKey: 'idOrig'
150
+ - oldKey: 'name'
151
+ targetKey: 'name'
152
+ - oldKey: 'breed'
153
+ targetKey: 'breed'
154
+ - oldKey: 'age'
155
+ targetKey: 'age'
156
+ - oldKey: 'ownerId'
157
+ targetKey: 'ownerIdOrig'
158
+ - oldKey: 'vetRecords'
159
+ targetKey: 'vetRecords'
160
+ converters: ['stringifyObject']
161
+ - oldKey: 'vetRecords.[any].id'
162
+ targetKey: 'vetRecordIds'
163
+ converters: ['anyToString']
164
+ - filePath: 'importData/dogs.json'
165
+ basePath: 'RECORDS'
166
+ type: 'update'
167
+ updateMapping: { originalIdField: 'id', targetField: 'idOrig' }
168
+ attributeMappings:
169
+ - oldKey: 'name'
170
+ targetKey: 'name'
171
+ - oldKey: 'breed'
172
+ targetKey: 'breed'
173
+ - oldKey: 'age'
174
+ targetKey: 'age'`;
175
+ const configFile = `# yaml-language-server: $schema=./.appwrite/appwriteUtilsConfigSchema.json
176
+ # Basic Appwrite configuration settings
177
+ appwriteEndpoint: 'https://cloud.appwrite.io/v1' # Your Appwrite endpoint
178
+ appwriteProject: 'YOUR_PROJECT_ID' # Your Appwrite project ID
179
+ appwriteKey: 'YOUR_API_KEY' # Your Appwrite API key (needs storage and databases at minimum)
180
+ enableDevDatabase: true # Enable development database alongside main. Default: true
181
+ enableBackups: true # Enable backups. Default: true
182
+ backupInterval: 3600 # Backup interval in seconds. Default: 3600 - DOES NOTHING RIGHT NOW
183
+ backupRetention: 30 # Backup retention in days. Default: 30 - DOES NOTHING RIGHT NOW
184
+ enableBackupCleanup: true # Enable backup cleanup. Default: true - DOES NOTHING RIGHT NOW
185
+ enableMockData: false # Enable mock data generation. Default: false - DOES NOTHING RIGHT NOW
186
+ enableWipeOtherDatabases: true # Enable wiping other databases. Default: true
187
+ documentBucketId: 'documents' # Your Appwrite bucket ID for documents. Default: 'documents'
188
+ usersCollectionName: 'Members' # Your Appwrite collection for any extra info while importing members (if any). Default: 'Members'
189
+ # Databases configuration
190
+ # The first one is *always* Production
191
+ # The second is *always* Staging
192
+ # The third is *always* Development
193
+ # They are found by name matching (without spaces and all lowercase), not $id
194
+ # If no $id is included for anything defined, Appwrite will auto-generate one in its stead
195
+ databases:
196
+ - $id: 'main' # Database ID
197
+ name: 'Main' # Database name
198
+ - $id: 'staging'
199
+ name: 'Staging'
200
+ - $id: 'dev'
201
+ name: 'Development'
202
+
203
+ # Collections configuration
204
+ collections:
205
+ - name: 'ExampleCollection' # Collection name
206
+ $permissions: # Permissions for the collection
207
+ - permission: read # Permission type
208
+ target: any # Permission target
209
+ - permission: create
210
+ target: users
211
+ - permission: update
212
+ target: users
213
+ - permission: delete
214
+ target: users
215
+ attributes: # Attributes of the collection
216
+ - key: 'exampleKey' # Attribute key
217
+ type: 'string' # Attribute type
218
+ size: 255 # Size of the attribute (for strings)
219
+ required: true # Whether the attribute is required`;
220
+ export const customDefinitionsFile = `import type { ConverterFunctions, ValidationRules, AfterImportActions } from "appwrite-utils";
221
+
222
+ export const customConverterFunctions: ConverterFunctions = {
223
+ // Add your custom converter functions here
224
+ }
225
+ export const customValidationRules: ValidationRules = {
226
+ // Add your custom validation rules here
227
+ }
228
+ export const customAfterImportActions: AfterImportActions = {
229
+ // Add your custom after import actions here
230
+ }`;
231
+ export const setupDirsFiles = async (example = false) => {
232
+ const basePath = process.cwd();
233
+ const srcPath = path.join(basePath, "src");
234
+ // Check if src directory exists in the current working directory
235
+ if (!existsSync(srcPath)) {
236
+ console.error("No 'src' directory found in the current working directory.");
237
+ return;
238
+ }
239
+ const appwriteFolder = path.join(srcPath, "appwrite");
240
+ const appwriteConfigFile = path.join(appwriteFolder, "appwriteConfig.yaml");
241
+ const appwriteCustomDefsFile = path.join(appwriteFolder, "customDefinitions.ts");
242
+ // const appwriteMigrationsFolder = path.join(appwriteFolder, "migrations");
243
+ const appwriteSchemaFolder = path.join(appwriteFolder, "schemas");
244
+ const appwriteDataFolder = path.join(appwriteFolder, "importData");
245
+ const appwriteHiddenFolder = path.join(appwriteFolder, ".appwrite");
246
+ // Directory creation and file writing logic remains the same
247
+ if (!existsSync(appwriteFolder)) {
248
+ mkdirSync(appwriteFolder, { recursive: true });
249
+ }
250
+ if (!existsSync(appwriteConfigFile)) {
251
+ if (example) {
252
+ writeFileSync(appwriteConfigFile, configFileExample);
253
+ }
254
+ else {
255
+ writeFileSync(appwriteConfigFile, configFile);
256
+ }
257
+ }
258
+ if (!existsSync(appwriteCustomDefsFile)) {
259
+ writeFileSync(appwriteCustomDefsFile, customDefinitionsFile);
260
+ }
261
+ // if (!existsSync(appwriteMigrationsFolder)) {
262
+ // mkdirSync(appwriteMigrationsFolder, { recursive: true });
263
+ // }
264
+ if (!existsSync(appwriteSchemaFolder)) {
265
+ mkdirSync(appwriteSchemaFolder, { recursive: true });
266
+ }
267
+ if (!existsSync(appwriteDataFolder)) {
268
+ mkdirSync(appwriteDataFolder, { recursive: true });
269
+ }
270
+ if (!existsSync(appwriteHiddenFolder)) {
271
+ mkdirSync(appwriteHiddenFolder, { recursive: true });
272
+ }
273
+ const schemaFilePath = path.join(appwriteHiddenFolder, "appwriteUtilsConfigSchema.json");
274
+ writeFileSync(schemaFilePath, JSON.stringify(configSchema, undefined, 2));
275
+ console.log("Created config and setup files/directories successfully.");
276
+ };
@@ -0,0 +1,30 @@
1
+ import { type ConverterFunctions } from "./migrations/converters.js";
2
+ import { type AfterImportActions } from "./migrations/afterImportActions.js";
3
+ import { type ValidationRules } from "./migrations/validationRules.js";
4
+ export interface SetupOptions {
5
+ runProd: boolean;
6
+ runStaging: boolean;
7
+ runDev: boolean;
8
+ doBackup: boolean;
9
+ wipeDatabases: boolean;
10
+ wipeDocumentStorage: boolean;
11
+ wipeUsers: boolean;
12
+ generateSchemas: boolean;
13
+ generateMockData: boolean;
14
+ importData: boolean;
15
+ checkDuplicates: boolean;
16
+ }
17
+ export declare class UtilsController {
18
+ private appwriteFolderPath;
19
+ private appwriteConfigPath;
20
+ private config?;
21
+ private appwriteServer?;
22
+ private database?;
23
+ private storage?;
24
+ converterDefinitions: ConverterFunctions;
25
+ validityRuleDefinitions: ValidationRules;
26
+ afterImportActionsDefinitions: AfterImportActions;
27
+ constructor();
28
+ init(): Promise<void>;
29
+ run(options: SetupOptions): Promise<void>;
30
+ }
@@ -0,0 +1,106 @@
1
+ import { Client, Databases, Storage } from "node-appwrite";
2
+ import { startSetup } from "./migrations/setupDatabase.js";
3
+ import { AppwriteConfigSchema, } from "./migrations/schema.js";
4
+ import path from "path";
5
+ import fs from "fs";
6
+ import { load } from "js-yaml";
7
+ import { ImportDataActions } from "./migrations/importDataActions.js";
8
+ import { converterFunctions, } from "./migrations/converters.js";
9
+ import { readFileSync } from "./utils/helperFunctions.js";
10
+ import { afterImportActions, } from "./migrations/afterImportActions.js";
11
+ import { validationRules, } from "./migrations/validationRules.js";
12
+ import { ImportController } from "./migrations/importController.js";
13
+ import _ from "lodash";
14
+ async function loadConfig(configPath) {
15
+ if (!fs.existsSync(configPath)) {
16
+ throw new Error(`Configuration file not found at ${configPath}`);
17
+ }
18
+ const configModule = await load(readFileSync(configPath), {
19
+ json: true,
20
+ });
21
+ return AppwriteConfigSchema.parse(configModule);
22
+ }
23
+ export class UtilsController {
24
+ appwriteFolderPath;
25
+ appwriteConfigPath;
26
+ config;
27
+ appwriteServer;
28
+ database;
29
+ storage;
30
+ converterDefinitions = converterFunctions;
31
+ validityRuleDefinitions = validationRules;
32
+ afterImportActionsDefinitions = afterImportActions;
33
+ constructor() {
34
+ const basePath = process.cwd(); // Gets the current working directory
35
+ const appwriteFolderPath = path.join(basePath, "src", "appwrite");
36
+ const appwriteConfigPath = path.join(appwriteFolderPath, "appwriteConfig.yaml");
37
+ this.appwriteFolderPath = appwriteFolderPath;
38
+ this.appwriteConfigPath = appwriteConfigPath;
39
+ }
40
+ // async loadCustomDefinitions(): Promise<void> {
41
+ // try {
42
+ // const customDefinitionsPath = path.join(
43
+ // this.appwriteFolderPath,
44
+ // "customDefinitions.ts"
45
+ // );
46
+ // if (fs.existsSync(customDefinitionsPath)) {
47
+ // // Dynamically import custom definitions
48
+ // const customDefinitions = (await import(
49
+ // customDefinitionsPath
50
+ // )) as typeof import("customDefinitions");
51
+ // this.converterDefinitions = {
52
+ // ...this.converterDefinitions,
53
+ // ...customDefinitions.converterDefinitions,
54
+ // };
55
+ // this.validityRuleDefinitions = {
56
+ // ...this.validityRuleDefinitions,
57
+ // ...customDefinitions.validityRuleDefinitions,
58
+ // };
59
+ // this.afterImportActionsDefinitions = {
60
+ // ...this.afterImportActionsDefinitions,
61
+ // ...customDefinitions.afterImportActionsDefinitions,
62
+ // };
63
+ // }
64
+ // } catch (error) {
65
+ // console.error("Failed to load custom definitions:", error);
66
+ // }
67
+ // }
68
+ async init() {
69
+ if (!this.config) {
70
+ console.log("Initializing appwrite client & loading config...");
71
+ this.config = await loadConfig(this.appwriteConfigPath);
72
+ this.appwriteServer = new Client()
73
+ .setEndpoint(this.config.appwriteEndpoint)
74
+ .setProject(this.config.appwriteProject)
75
+ .setKey(this.config.appwriteKey);
76
+ this.database = new Databases(this.appwriteServer);
77
+ this.storage = new Storage(this.appwriteServer);
78
+ this.config.appwriteClient = this.appwriteServer;
79
+ // await this.loadCustomDefinitions();
80
+ }
81
+ }
82
+ async run(options) {
83
+ await this.init(); // Ensure initialization is done
84
+ if (!this.database || !this.storage || !this.config) {
85
+ throw new Error("Database or storage not initialized");
86
+ }
87
+ // Start the setup
88
+ console.log("Starting setup, this step sets up migrations, runs backup, wipes databases, and updates schemas (depending on your options)...");
89
+ await startSetup(this.database, this.storage, this.config, options, this.appwriteFolderPath);
90
+ console.log("Setup complete.");
91
+ if (options.generateMockData) {
92
+ // TODO: Figure out how to do this dynamically
93
+ // await this.generateMockData();
94
+ }
95
+ if (options.importData) {
96
+ console.log("Starting import data...");
97
+ const importDataActions = new ImportDataActions(this.database, this.storage, this.config, this.converterDefinitions, this.validityRuleDefinitions, this.afterImportActionsDefinitions);
98
+ const importController = new ImportController(this.config, this.database, this.storage, this.appwriteFolderPath, importDataActions, options);
99
+ await importController.run();
100
+ console.log("Import data complete.");
101
+ }
102
+ // if (options.checkDuplicates) {
103
+ // await this.checkDuplicates();
104
+ // }
105
+ }
106
+ }
package/package.json ADDED
@@ -0,0 +1,34 @@
1
+ {
2
+ "name": "appwrite-utils-cli",
3
+ "description": "Appwrite Utility Functions to help with database management, data conversion, data import, migrations, and much more. Meant to be used as a CLI tool, I do not recommend installing this in frontend environments.",
4
+ "version": "0.0.1",
5
+ "main": "src/main.ts",
6
+ "type": "module",
7
+ "repository": {
8
+ "type": "git",
9
+ "url": "https://github.com/zachhandley/AppwriteUtils"
10
+ },
11
+ "bin": {
12
+ "appwrite-setup": "./dist/setup.js",
13
+ "appwrite-migrate": "./dist/main.js"
14
+ },
15
+ "scripts": {
16
+ "build": "bun run tsc",
17
+ "deploy": "bun run build && npm publish --access public",
18
+ "postinstall": "echo 'This package is intended for CLI use only and should not be added as a dependency in other projects.'"
19
+ },
20
+ "dependencies": {
21
+ "js-yaml": "^4.1.0",
22
+ "lodash": "^4.17.21",
23
+ "luxon": "^3.4.4",
24
+ "node-appwrite": "^12.0.1",
25
+ "winston": "^3.13.0",
26
+ "zod": "^3.22.4"
27
+ },
28
+ "devDependencies": {
29
+ "@types/js-yaml": "^4.0.9",
30
+ "@types/lodash": "^4.17.0",
31
+ "@types/luxon": "^3.4.2",
32
+ "typescript": "^5.0.0"
33
+ }
34
+ }
package/src/main.ts ADDED
@@ -0,0 +1,77 @@
1
+ #!/usr/bin/env node
2
+ import { UtilsController } from "./utilsController.js";
3
+
4
+ const args = process.argv.slice(2);
5
+
6
+ async function main() {
7
+ const controller = new UtilsController();
8
+ await controller.init();
9
+
10
+ let runProd = false;
11
+ let runStaging = false;
12
+ let runDev = false;
13
+ let doBackup = false;
14
+ let wipeDatabases = false;
15
+ let wipeUsers = false;
16
+ let generateSchemas = false;
17
+ let importData = false;
18
+ let wipeDocuments = false;
19
+ if (args.includes("--prod")) {
20
+ runProd = true;
21
+ }
22
+ if (args.includes("--staging")) {
23
+ runStaging = true;
24
+ }
25
+ if (args.includes("--dev")) {
26
+ runDev = true;
27
+ }
28
+ if (args.includes("--wipe")) {
29
+ wipeDatabases = true;
30
+ }
31
+ if (args.includes("--wipe-docs") || args.includes("--wipeDocs")) {
32
+ wipeDocuments = true;
33
+ }
34
+ if (args.includes("--generate")) {
35
+ generateSchemas = true;
36
+ }
37
+ if (args.includes("--import")) {
38
+ importData = true;
39
+ }
40
+ if (args.includes("--backup")) {
41
+ doBackup = true;
42
+ }
43
+ if (args.includes("--wipe-users") || args.includes("--wipeUsers")) {
44
+ wipeUsers = true;
45
+ }
46
+ if (args.includes("--init")) {
47
+ await controller.run({
48
+ runProd: runProd,
49
+ runStaging: runStaging,
50
+ runDev: runDev,
51
+ doBackup: doBackup,
52
+ wipeDatabases: wipeDatabases,
53
+ wipeUsers: wipeUsers,
54
+ wipeDocumentStorage: wipeDocuments,
55
+ generateSchemas: true,
56
+ generateMockData: false,
57
+ importData: false,
58
+ checkDuplicates: false,
59
+ });
60
+ } else {
61
+ await controller.run({
62
+ runProd: runProd,
63
+ runStaging: runStaging,
64
+ runDev: runDev,
65
+ doBackup: doBackup,
66
+ wipeDatabases: wipeDatabases,
67
+ wipeDocumentStorage: wipeDocuments,
68
+ generateSchemas: generateSchemas,
69
+ generateMockData: false,
70
+ wipeUsers: wipeUsers,
71
+ importData: importData,
72
+ checkDuplicates: false,
73
+ });
74
+ }
75
+ }
76
+
77
+ main().catch(console.error);