appwrite-utils-cli 0.10.75 → 0.10.77

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 CHANGED
@@ -115,6 +115,8 @@ npx appwrite-utils-cli appwrite-migrate --transfer --fromBucketId sourceBucketId
115
115
 
116
116
  ### Update Function Specifications
117
117
 
118
+ **NOTE: IF IT DOES NOT WORK AND YOU ARE SELF-HOSTED, PLEASE SET MAX CPU_PER_FUNCTION AND RAM_PER_FUNCTION IN YOUR ENV VARS TO > 0, THIS IS A BUG IN 1.6.0**
119
+
118
120
  Update the CPU and RAM specifications for a function:
119
121
 
120
122
  ```bash
@@ -148,6 +150,8 @@ This updated CLI ensures that developers have robust tools at their fingertips t
148
150
 
149
151
  ## Changelog
150
152
 
153
+ - 0.10.77: Added disclaimer to update function specifications for bug
154
+ - 0.10.76: Updated CLI commands to not require an `appwriteConfig.ts` if you set API Key, ProjectId, and Endpoint
151
155
  - 0.10.75: Fixed slight issue writing ZOD Schema for collection without any attributes
152
156
  - 0.10.74: Fix moving users, should update Labels now
153
157
  - 0.10.73: Fix moving users, passwords should work now (from Appwrite, Argon2)
@@ -122,9 +122,9 @@ export class InteractiveCLI {
122
122
  }
123
123
  }
124
124
  }
125
- async initControllerIfNeeded() {
125
+ async initControllerIfNeeded(directConfig) {
126
126
  if (!this.controller) {
127
- this.controller = new UtilsController(this.currentDir);
127
+ this.controller = new UtilsController(this.currentDir, directConfig);
128
128
  await this.controller.init();
129
129
  }
130
130
  }
package/dist/main.js CHANGED
@@ -159,12 +159,23 @@ async function main() {
159
159
  await cli.run();
160
160
  }
161
161
  else {
162
- const controller = new UtilsController(process.cwd());
162
+ const directConfig = argv.endpoint || argv.projectId || argv.apiKey
163
+ ? {
164
+ appwriteEndpoint: argv.endpoint,
165
+ appwriteProject: argv.projectId,
166
+ appwriteKey: argv.apiKey,
167
+ }
168
+ : undefined;
169
+ const controller = new UtilsController(process.cwd(), directConfig);
163
170
  await controller.init();
164
171
  if (argv.setup) {
165
172
  await setupDirsFiles(false, process.cwd());
166
173
  return;
167
174
  }
175
+ if (!controller.config) {
176
+ console.log(chalk.red("No Appwrite connection found"));
177
+ return;
178
+ }
168
179
  const parsedArgv = argv;
169
180
  const options = {
170
181
  databases: parsedArgv.dbIds
@@ -280,7 +291,11 @@ async function main() {
280
291
  // Only fetch databases if database IDs are provided
281
292
  if (parsedArgv.fromDbId && parsedArgv.toDbId) {
282
293
  console.log(chalk.blue(`Starting database transfer from ${parsedArgv.fromDbId} to ${parsedArgv.toDbId}`));
283
- fromDb = (await controller.getDatabasesByIds([parsedArgv.fromDbId]))[0];
294
+ fromDb = (await controller.getDatabasesByIds([parsedArgv.fromDbId]))?.[0];
295
+ if (!fromDb) {
296
+ console.log(chalk.red("Source database not found"));
297
+ return;
298
+ }
284
299
  if (isRemote) {
285
300
  if (!parsedArgv.remoteEndpoint ||
286
301
  !parsedArgv.remoteProjectId ||
@@ -292,12 +307,21 @@ async function main() {
292
307
  targetStorage = new Storage(remoteClient);
293
308
  const remoteDbs = await fetchAllDatabases(targetDatabases);
294
309
  toDb = remoteDbs.find((db) => db.$id === parsedArgv.toDbId);
310
+ if (!toDb) {
311
+ console.log(chalk.red("Target database not found"));
312
+ return;
313
+ }
295
314
  }
296
315
  else {
297
- toDb = (await controller.getDatabasesByIds([parsedArgv.toDbId]))[0];
316
+ toDb = (await controller.getDatabasesByIds([parsedArgv.toDbId]))?.[0];
317
+ if (!toDb) {
318
+ console.log(chalk.red("Target database not found"));
319
+ return;
320
+ }
298
321
  }
299
322
  if (!fromDb || !toDb) {
300
- throw new Error("Source or target database not found");
323
+ console.log(chalk.red("Source or target database not found"));
324
+ return;
301
325
  }
302
326
  }
303
327
  // Handle storage setup
@@ -17,8 +17,8 @@ export interface SetupOptions {
17
17
  shouldWriteFile?: boolean;
18
18
  }
19
19
  export declare class UtilsController {
20
- private appwriteFolderPath;
21
- private appwriteConfigPath;
20
+ private appwriteFolderPath?;
21
+ private appwriteConfigPath?;
22
22
  config?: AppwriteConfig;
23
23
  appwriteServer?: Client;
24
24
  database?: Databases;
@@ -26,14 +26,18 @@ export declare class UtilsController {
26
26
  converterDefinitions: ConverterFunctions;
27
27
  validityRuleDefinitions: ValidationRules;
28
28
  afterImportActionsDefinitions: AfterImportActions;
29
- constructor(currentUserDir: string);
29
+ constructor(currentUserDir: string, directConfig?: {
30
+ appwriteEndpoint?: string;
31
+ appwriteProject?: string;
32
+ appwriteKey?: string;
33
+ });
30
34
  init(): Promise<void>;
31
35
  reloadConfig(): Promise<void>;
32
36
  setupMigrationDatabase(): Promise<void>;
33
37
  ensureDatabaseConfigBucketsExist(databases?: Models.Database[]): Promise<void>;
34
38
  ensureDatabasesExist(databases?: Models.Database[]): Promise<void>;
35
39
  ensureCollectionsExist(database: Models.Database, collections?: Models.Collection[]): Promise<void>;
36
- getDatabasesByIds(ids: string[]): Promise<Models.Database[]>;
40
+ getDatabasesByIds(ids: string[]): Promise<Models.Database[] | undefined>;
37
41
  wipeOtherDatabases(databasesToKeep: Models.Database[]): Promise<void>;
38
42
  wipeUsers(): Promise<void>;
39
43
  backupDatabase(database: Models.Database): Promise<void>;
@@ -54,7 +58,7 @@ export declare class UtilsController {
54
58
  importData(options?: SetupOptions): Promise<void>;
55
59
  synchronizeConfigurations(databases?: Models.Database[], config?: AppwriteConfig): Promise<void>;
56
60
  syncDb(databases?: Models.Database[], collections?: Models.Collection[]): Promise<void>;
57
- getAppwriteFolderPath(): string;
61
+ getAppwriteFolderPath(): string | undefined;
58
62
  transferData(options: TransferOptions): Promise<void>;
59
63
  updateFunctionSpecifications(functionId: string, specification: Specification): Promise<void>;
60
64
  }
@@ -28,37 +28,87 @@ export class UtilsController {
28
28
  converterDefinitions = converterFunctions;
29
29
  validityRuleDefinitions = validationRules;
30
30
  afterImportActionsDefinitions = afterImportActions;
31
- constructor(currentUserDir) {
31
+ constructor(currentUserDir, directConfig) {
32
32
  const basePath = currentUserDir;
33
- const appwriteConfigFound = findAppwriteConfig(basePath);
34
- if (!appwriteConfigFound) {
35
- throw new Error("Failed to find appwriteConfig.ts");
33
+ if (directConfig) {
34
+ let hasErrors = false;
35
+ if (!directConfig.appwriteEndpoint) {
36
+ console.log(chalk.red("Appwrite endpoint is required"));
37
+ hasErrors = true;
38
+ }
39
+ if (!directConfig.appwriteProject) {
40
+ console.log(chalk.red("Appwrite project is required"));
41
+ hasErrors = true;
42
+ }
43
+ if (!directConfig.appwriteKey) {
44
+ console.log(chalk.red("Appwrite key is required"));
45
+ hasErrors = true;
46
+ }
47
+ if (!hasErrors) {
48
+ // Only set config if we have all required fields
49
+ this.appwriteFolderPath = basePath;
50
+ this.config = {
51
+ appwriteEndpoint: directConfig.appwriteEndpoint,
52
+ appwriteProject: directConfig.appwriteProject,
53
+ appwriteKey: directConfig.appwriteKey,
54
+ enableBackups: false,
55
+ backupInterval: 0,
56
+ backupRetention: 0,
57
+ enableBackupCleanup: false,
58
+ enableMockData: false,
59
+ documentBucketId: "",
60
+ usersCollectionName: "",
61
+ databases: [],
62
+ buckets: [],
63
+ functions: [],
64
+ };
65
+ }
36
66
  }
37
- this.appwriteConfigPath = appwriteConfigFound;
38
- this.appwriteFolderPath = path.dirname(appwriteConfigFound);
39
- if (!this.appwriteFolderPath) {
40
- throw new Error("Failed to get appwriteFolderPath");
67
+ else {
68
+ // Try to find config file
69
+ const appwriteConfigFound = findAppwriteConfig(basePath);
70
+ if (!appwriteConfigFound) {
71
+ console.log(chalk.yellow("No appwriteConfig.ts found and no direct configuration provided"));
72
+ return;
73
+ }
74
+ this.appwriteConfigPath = appwriteConfigFound;
75
+ this.appwriteFolderPath = path.dirname(appwriteConfigFound);
41
76
  }
42
77
  }
43
78
  async init() {
44
79
  if (!this.config) {
45
- console.log("Initializing appwrite client & loading config...");
46
- this.config = await loadConfig(this.appwriteFolderPath);
47
- if (!this.config) {
48
- throw new Error("Failed to load config");
80
+ if (this.appwriteFolderPath && this.appwriteConfigPath) {
81
+ console.log("Loading config from file...");
82
+ this.config = await loadConfig(this.appwriteFolderPath);
83
+ if (!this.config) {
84
+ console.log(chalk.red("Failed to load config from file"));
85
+ return;
86
+ }
87
+ }
88
+ else {
89
+ console.log(chalk.red("No configuration available"));
90
+ return;
49
91
  }
50
- this.appwriteServer = new Client();
51
- this.appwriteServer
52
- .setEndpoint(this.config.appwriteEndpoint)
53
- .setProject(this.config.appwriteProject)
54
- .setKey(this.config.appwriteKey);
55
- this.database = new Databases(this.appwriteServer);
56
- this.storage = new Storage(this.appwriteServer);
57
- this.config.appwriteClient = this.appwriteServer;
58
92
  }
93
+ this.appwriteServer = new Client();
94
+ this.appwriteServer
95
+ .setEndpoint(this.config.appwriteEndpoint)
96
+ .setProject(this.config.appwriteProject)
97
+ .setKey(this.config.appwriteKey);
98
+ this.database = new Databases(this.appwriteServer);
99
+ this.storage = new Storage(this.appwriteServer);
100
+ this.config.appwriteClient = this.appwriteServer;
59
101
  }
60
102
  async reloadConfig() {
103
+ if (!this.appwriteFolderPath) {
104
+ console.log(chalk.red("Failed to get appwriteFolderPath"));
105
+ return;
106
+ }
61
107
  this.config = await loadConfig(this.appwriteFolderPath);
108
+ if (!this.config) {
109
+ console.log(chalk.red("Failed to load config"));
110
+ return;
111
+ }
62
112
  this.appwriteServer = new Client();
63
113
  this.appwriteServer
64
114
  .setEndpoint(this.config.appwriteEndpoint)
@@ -70,36 +120,48 @@ export class UtilsController {
70
120
  }
71
121
  async setupMigrationDatabase() {
72
122
  await this.init();
73
- if (!this.config)
74
- throw new Error("Config not initialized");
123
+ if (!this.config) {
124
+ console.log(chalk.red("Config not initialized"));
125
+ return;
126
+ }
75
127
  await setupMigrationDatabase(this.config);
76
128
  }
77
129
  async ensureDatabaseConfigBucketsExist(databases = []) {
78
130
  await this.init();
79
- if (!this.storage)
80
- throw new Error("Storage not initialized");
81
- if (!this.config)
82
- throw new Error("Config not initialized");
131
+ if (!this.storage) {
132
+ console.log(chalk.red("Storage not initialized"));
133
+ return;
134
+ }
135
+ if (!this.config) {
136
+ console.log(chalk.red("Config not initialized"));
137
+ return;
138
+ }
83
139
  await ensureDatabaseConfigBucketsExist(this.storage, this.config, databases);
84
140
  }
85
141
  async ensureDatabasesExist(databases) {
86
142
  await this.init();
87
- if (!this.config)
88
- throw new Error("Config not initialized");
143
+ if (!this.config) {
144
+ console.log(chalk.red("Config not initialized"));
145
+ return;
146
+ }
89
147
  await this.setupMigrationDatabase();
90
148
  await this.ensureDatabaseConfigBucketsExist(databases);
91
149
  await ensureDatabasesExist(this.config, databases);
92
150
  }
93
151
  async ensureCollectionsExist(database, collections) {
94
152
  await this.init();
95
- if (!this.config)
96
- throw new Error("Config not initialized");
153
+ if (!this.config) {
154
+ console.log(chalk.red("Config not initialized"));
155
+ return;
156
+ }
97
157
  await ensureCollectionsExist(this.config, database, collections);
98
158
  }
99
159
  async getDatabasesByIds(ids) {
100
160
  await this.init();
101
- if (!this.database)
102
- throw new Error("Database not initialized");
161
+ if (!this.database) {
162
+ console.log(chalk.red("Database not initialized"));
163
+ return;
164
+ }
103
165
  if (ids.length === 0)
104
166
  return [];
105
167
  const dbs = await this.database.list([
@@ -110,36 +172,50 @@ export class UtilsController {
110
172
  }
111
173
  async wipeOtherDatabases(databasesToKeep) {
112
174
  await this.init();
113
- if (!this.database)
114
- throw new Error("Database not initialized");
175
+ if (!this.database) {
176
+ console.log(chalk.red("Database not initialized"));
177
+ return;
178
+ }
115
179
  await wipeOtherDatabases(this.database, databasesToKeep);
116
180
  }
117
181
  async wipeUsers() {
118
182
  await this.init();
119
- if (!this.config || !this.database)
120
- throw new Error("Config or database not initialized");
183
+ if (!this.config || !this.database) {
184
+ console.log(chalk.red("Config or database not initialized"));
185
+ return;
186
+ }
121
187
  const usersController = new UsersController(this.config, this.database);
122
188
  await usersController.wipeUsers();
123
189
  }
124
190
  async backupDatabase(database) {
125
191
  await this.init();
126
- if (!this.database || !this.storage || !this.config)
127
- throw new Error("Database, storage, or config not initialized");
192
+ if (!this.database || !this.storage || !this.config) {
193
+ console.log(chalk.red("Database, storage, or config not initialized"));
194
+ return;
195
+ }
128
196
  await backupDatabase(this.config, this.database, database.$id, this.storage);
129
197
  }
130
198
  async listAllFunctions() {
131
199
  await this.init();
132
- if (!this.appwriteServer)
133
- throw new Error("Appwrite server not initialized");
200
+ if (!this.appwriteServer) {
201
+ console.log(chalk.red("Appwrite server not initialized"));
202
+ return [];
203
+ }
134
204
  const { functions } = await listFunctions(this.appwriteServer, [
135
205
  Query.limit(1000),
136
206
  ]);
137
207
  return functions;
138
208
  }
139
209
  async findFunctionDirectories() {
210
+ if (!this.appwriteFolderPath) {
211
+ console.log(chalk.red("Failed to get appwriteFolderPath"));
212
+ return new Map();
213
+ }
140
214
  const functionsDir = findFunctionsDir(this.appwriteFolderPath);
141
- if (!functionsDir)
215
+ if (!functionsDir) {
216
+ console.log(chalk.red("Failed to find functions directory"));
142
217
  return new Map();
218
+ }
143
219
  const functionDirMap = new Map();
144
220
  const entries = fs.readdirSync(functionsDir, { withFileTypes: true });
145
221
  for (const entry of entries) {
@@ -158,19 +234,25 @@ export class UtilsController {
158
234
  }
159
235
  async deployFunction(functionName, functionPath, functionConfig) {
160
236
  await this.init();
161
- if (!this.appwriteServer)
162
- throw new Error("Appwrite server not initialized");
237
+ if (!this.appwriteServer) {
238
+ console.log(chalk.red("Appwrite server not initialized"));
239
+ return;
240
+ }
163
241
  if (!functionConfig) {
164
242
  functionConfig = this.config?.functions?.find((f) => f.name === functionName);
165
243
  }
166
- if (!functionConfig)
167
- throw new Error(`Function ${functionName} not found in config`);
244
+ if (!functionConfig) {
245
+ console.log(chalk.red(`Function ${functionName} not found in config`));
246
+ return;
247
+ }
168
248
  await deployLocalFunction(this.appwriteServer, functionName, functionConfig, functionPath);
169
249
  }
170
250
  async syncFunctions() {
171
251
  await this.init();
172
- if (!this.appwriteServer)
173
- throw new Error("Appwrite server not initialized");
252
+ if (!this.appwriteServer) {
253
+ console.log(chalk.red("Appwrite server not initialized"));
254
+ return;
255
+ }
174
256
  const localFunctions = this.config?.functions || [];
175
257
  const remoteFunctions = await listFunctions(this.appwriteServer, [
176
258
  Query.limit(1000),
@@ -243,36 +325,62 @@ export class UtilsController {
243
325
  }
244
326
  async generateSchemas() {
245
327
  await this.init();
246
- if (!this.config)
247
- throw new Error("Config not initialized");
328
+ if (!this.config) {
329
+ console.log(chalk.red("Config not initialized"));
330
+ return;
331
+ }
332
+ if (!this.appwriteFolderPath) {
333
+ console.log(chalk.red("Failed to get appwriteFolderPath"));
334
+ return;
335
+ }
248
336
  await generateSchemas(this.config, this.appwriteFolderPath);
249
337
  }
250
338
  async importData(options = {}) {
251
339
  await this.init();
252
- if (!this.database)
253
- throw new Error("Database not initialized");
254
- if (!this.storage)
255
- throw new Error("Storage not initialized");
256
- if (!this.config)
257
- throw new Error("Config not initialized");
340
+ if (!this.database) {
341
+ console.log(chalk.red("Database not initialized"));
342
+ return;
343
+ }
344
+ if (!this.storage) {
345
+ console.log(chalk.red("Storage not initialized"));
346
+ return;
347
+ }
348
+ if (!this.config) {
349
+ console.log(chalk.red("Config not initialized"));
350
+ return;
351
+ }
352
+ if (!this.appwriteFolderPath) {
353
+ console.log(chalk.red("Failed to get appwriteFolderPath"));
354
+ return;
355
+ }
258
356
  const importDataActions = new ImportDataActions(this.database, this.storage, this.config, this.converterDefinitions, this.validityRuleDefinitions, this.afterImportActionsDefinitions);
259
357
  const importController = new ImportController(this.config, this.database, this.storage, this.appwriteFolderPath, importDataActions, options, options.databases);
260
358
  await importController.run(options.collections);
261
359
  }
262
360
  async synchronizeConfigurations(databases, config) {
263
361
  await this.init();
264
- if (!this.storage)
265
- throw new Error("Storage not initialized");
362
+ if (!this.storage) {
363
+ console.log(chalk.red("Storage not initialized"));
364
+ return;
365
+ }
266
366
  const configToUse = config || this.config;
267
- if (!configToUse)
268
- throw new Error("Config not initialized");
367
+ if (!configToUse) {
368
+ console.log(chalk.red("Config not initialized"));
369
+ return;
370
+ }
371
+ if (!this.appwriteFolderPath) {
372
+ console.log(chalk.red("Failed to get appwriteFolderPath"));
373
+ return;
374
+ }
269
375
  const appwriteToX = new AppwriteToX(configToUse, this.appwriteFolderPath, this.storage);
270
376
  await appwriteToX.toSchemas(databases);
271
377
  }
272
378
  async syncDb(databases = [], collections = []) {
273
379
  await this.init();
274
- if (!this.database)
275
- throw new Error("Database not initialized");
380
+ if (!this.database) {
381
+ console.log(chalk.red("Database not initialized"));
382
+ return;
383
+ }
276
384
  if (databases.length === 0) {
277
385
  const allDatabases = await fetchAllDatabases(this.database);
278
386
  databases = allDatabases;
@@ -290,13 +398,15 @@ export class UtilsController {
290
398
  let sourceDatabases = [];
291
399
  let targetDatabases = [];
292
400
  if (!sourceClient) {
293
- throw new Error("Source database not initialized");
401
+ console.log(chalk.red("Source database not initialized"));
402
+ return;
294
403
  }
295
404
  if (options.isRemote) {
296
405
  if (!options.transferEndpoint ||
297
406
  !options.transferProject ||
298
407
  !options.transferKey) {
299
- throw new Error("Remote transfer options are missing");
408
+ console.log(chalk.red("Remote transfer options are missing"));
409
+ return;
300
410
  }
301
411
  const remoteClient = getClient(options.transferEndpoint, options.transferProject, options.transferKey);
302
412
  targetClient = new Databases(remoteClient);
@@ -312,7 +422,8 @@ export class UtilsController {
312
422
  const fromDb = sourceDatabases.find((db) => db.$id === options.fromDb.$id);
313
423
  const targetDb = targetDatabases.find((db) => db.$id === options.targetDb.$id);
314
424
  if (!fromDb || !targetDb) {
315
- throw new Error("Source or target database not found");
425
+ console.log(chalk.red("Source or target database not found"));
426
+ return;
316
427
  }
317
428
  if (options.isRemote && targetClient) {
318
429
  await transferDatabaseLocalToRemote(sourceClient, options.transferEndpoint, options.transferProject, options.transferKey, fromDb.$id, targetDb.$id);
@@ -326,7 +437,8 @@ export class UtilsController {
326
437
  console.log(chalk.yellow("User transfer is only supported for remote transfers. Skipping..."));
327
438
  }
328
439
  else if (!this.appwriteServer) {
329
- throw new Error("Appwrite server not initialized");
440
+ console.log(chalk.red("Appwrite server not initialized"));
441
+ return;
330
442
  }
331
443
  else {
332
444
  console.log(chalk.blue("Starting user transfer..."));
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "appwrite-utils-cli",
3
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.10.75",
4
+ "version": "0.10.77",
5
5
  "main": "src/main.ts",
6
6
  "type": "module",
7
7
  "repository": {
@@ -159,9 +159,13 @@ export class InteractiveCLI {
159
159
  }
160
160
  }
161
161
 
162
- private async initControllerIfNeeded(): Promise<void> {
162
+ private async initControllerIfNeeded(directConfig?: {
163
+ appwriteEndpoint: string;
164
+ appwriteProject: string;
165
+ appwriteKey: string;
166
+ }): Promise<void> {
163
167
  if (!this.controller) {
164
- this.controller = new UtilsController(this.currentDir);
168
+ this.controller = new UtilsController(this.currentDir, directConfig);
165
169
  await this.controller.init();
166
170
  }
167
171
  }
@@ -522,7 +526,7 @@ export class InteractiveCLI {
522
526
  functionConfig.dirPath,
523
527
  // 2. Appwrite config folder/functions/name
524
528
  join(
525
- this.controller.getAppwriteFolderPath(),
529
+ this.controller.getAppwriteFolderPath()!,
526
530
  "functions",
527
531
  functionNameLower
528
532
  ),
@@ -553,7 +557,7 @@ export class InteractiveCLI {
553
557
 
554
558
  // Search in both appwrite config directory and current working directory
555
559
  functionPath = await this.findFunctionInSubdirectories(
556
- [this.controller.getAppwriteFolderPath(), process.cwd()],
560
+ [this.controller.getAppwriteFolderPath()!, process.cwd()],
557
561
  functionNameLower
558
562
  );
559
563
  }
@@ -576,7 +580,7 @@ export class InteractiveCLI {
576
580
  await downloadLatestFunctionDeployment(
577
581
  this.controller.appwriteServer!,
578
582
  functionConfig.$id,
579
- join(this.controller.getAppwriteFolderPath(), "functions")
583
+ join(this.controller.getAppwriteFolderPath()!, "functions")
580
584
  );
581
585
  console.log(
582
586
  chalk.green(`✨ Function downloaded to ${downloadedPath}`)
@@ -1140,7 +1144,7 @@ export class InteractiveCLI {
1140
1144
  if (hasLocal && hasRemote) {
1141
1145
  // First try to find the function locally
1142
1146
  let functionPath = join(
1143
- this.controller!.getAppwriteFolderPath(),
1147
+ this.controller!.getAppwriteFolderPath()!,
1144
1148
  "functions",
1145
1149
  func.name
1146
1150
  );
@@ -1152,7 +1156,7 @@ export class InteractiveCLI {
1152
1156
  )
1153
1157
  );
1154
1158
  const foundPath = await this.findFunctionInSubdirectories(
1155
- [this.controller!.getAppwriteFolderPath(), process.cwd()],
1159
+ [this.controller!.getAppwriteFolderPath()!, process.cwd()],
1156
1160
  func.name
1157
1161
  );
1158
1162
 
@@ -1191,7 +1195,7 @@ export class InteractiveCLI {
1191
1195
  await downloadLatestFunctionDeployment(
1192
1196
  this.controller!.appwriteServer!,
1193
1197
  func.$id,
1194
- join(this.controller!.getAppwriteFolderPath(), "functions")
1198
+ join(this.controller!.getAppwriteFolderPath()!, "functions")
1195
1199
  );
1196
1200
  } else if (preference === "config") {
1197
1201
  const remoteFunction = await getFunction(
@@ -1236,14 +1240,14 @@ export class InteractiveCLI {
1236
1240
  } else if (hasLocal) {
1237
1241
  // Similar check for local-only functions
1238
1242
  let functionPath = join(
1239
- this.controller!.getAppwriteFolderPath(),
1243
+ this.controller!.getAppwriteFolderPath()!,
1240
1244
  "functions",
1241
1245
  func.name
1242
1246
  );
1243
1247
 
1244
1248
  if (!fs.existsSync(functionPath)) {
1245
1249
  const foundPath = await this.findFunctionInSubdirectories(
1246
- [this.controller!.getAppwriteFolderPath(), process.cwd()],
1250
+ [this.controller!.getAppwriteFolderPath()!, process.cwd()],
1247
1251
  func.name
1248
1252
  );
1249
1253
 
@@ -1294,7 +1298,7 @@ export class InteractiveCLI {
1294
1298
  await downloadLatestFunctionDeployment(
1295
1299
  this.controller!.appwriteServer!,
1296
1300
  func.$id,
1297
- join(this.controller!.getAppwriteFolderPath(), "functions")
1301
+ join(this.controller!.getAppwriteFolderPath()!, "functions")
1298
1302
  );
1299
1303
  } else if (action === "config") {
1300
1304
  const remoteFunction = await getFunction(
@@ -1336,7 +1340,7 @@ export class InteractiveCLI {
1336
1340
  // Update schemas after all changes
1337
1341
  const schemaGenerator = new SchemaGenerator(
1338
1342
  this.controller!.config!,
1339
- this.controller!.getAppwriteFolderPath()
1343
+ this.controller!.getAppwriteFolderPath()!
1340
1344
  );
1341
1345
  schemaGenerator.updateConfig(this.controller!.config!);
1342
1346
  }
package/src/main.ts CHANGED
@@ -202,7 +202,15 @@ async function main() {
202
202
  const cli = new InteractiveCLI(process.cwd());
203
203
  await cli.run();
204
204
  } else {
205
- const controller = new UtilsController(process.cwd());
205
+ const directConfig =
206
+ argv.endpoint || argv.projectId || argv.apiKey
207
+ ? {
208
+ appwriteEndpoint: argv.endpoint,
209
+ appwriteProject: argv.projectId,
210
+ appwriteKey: argv.apiKey,
211
+ }
212
+ : undefined;
213
+ const controller = new UtilsController(process.cwd(), directConfig);
206
214
  await controller.init();
207
215
 
208
216
  if (argv.setup) {
@@ -210,6 +218,11 @@ async function main() {
210
218
  return;
211
219
  }
212
220
 
221
+ if (!controller.config) {
222
+ console.log(chalk.red("No Appwrite connection found"));
223
+ return;
224
+ }
225
+
213
226
  const parsedArgv = argv;
214
227
 
215
228
  const options: SetupOptions = {
@@ -372,8 +385,13 @@ async function main() {
372
385
  `Starting database transfer from ${parsedArgv.fromDbId} to ${parsedArgv.toDbId}`
373
386
  )
374
387
  );
375
- fromDb = (await controller.getDatabasesByIds([parsedArgv.fromDbId]))[0];
376
-
388
+ fromDb = (
389
+ await controller.getDatabasesByIds([parsedArgv.fromDbId])
390
+ )?.[0];
391
+ if (!fromDb) {
392
+ console.log(chalk.red("Source database not found"));
393
+ return;
394
+ }
377
395
  if (isRemote) {
378
396
  if (
379
397
  !parsedArgv.remoteEndpoint ||
@@ -391,12 +409,21 @@ async function main() {
391
409
  targetStorage = new Storage(remoteClient);
392
410
  const remoteDbs = await fetchAllDatabases(targetDatabases);
393
411
  toDb = remoteDbs.find((db) => db.$id === parsedArgv.toDbId);
412
+ if (!toDb) {
413
+ console.log(chalk.red("Target database not found"));
414
+ return;
415
+ }
394
416
  } else {
395
- toDb = (await controller.getDatabasesByIds([parsedArgv.toDbId]))[0];
417
+ toDb = (await controller.getDatabasesByIds([parsedArgv.toDbId]))?.[0];
418
+ if (!toDb) {
419
+ console.log(chalk.red("Target database not found"));
420
+ return;
421
+ }
396
422
  }
397
423
 
398
424
  if (!fromDb || !toDb) {
399
- throw new Error("Source or target database not found");
425
+ console.log(chalk.red("Source or target database not found"));
426
+ return;
400
427
  }
401
428
  }
402
429
 
@@ -82,8 +82,8 @@ export interface SetupOptions {
82
82
  }
83
83
 
84
84
  export class UtilsController {
85
- private appwriteFolderPath: string;
86
- private appwriteConfigPath: string;
85
+ private appwriteFolderPath?: string;
86
+ private appwriteConfigPath?: string;
87
87
  public config?: AppwriteConfig;
88
88
  public appwriteServer?: Client;
89
89
  public database?: Databases;
@@ -92,39 +92,101 @@ export class UtilsController {
92
92
  public validityRuleDefinitions: ValidationRules = validationRules;
93
93
  public afterImportActionsDefinitions: AfterImportActions = afterImportActions;
94
94
 
95
- constructor(currentUserDir: string) {
96
- const basePath = currentUserDir;
97
- const appwriteConfigFound = findAppwriteConfig(basePath);
98
- if (!appwriteConfigFound) {
99
- throw new Error("Failed to find appwriteConfig.ts");
95
+ constructor(
96
+ currentUserDir: string,
97
+ directConfig?: {
98
+ appwriteEndpoint?: string;
99
+ appwriteProject?: string;
100
+ appwriteKey?: string;
100
101
  }
101
- this.appwriteConfigPath = appwriteConfigFound;
102
- this.appwriteFolderPath = path.dirname(appwriteConfigFound);
103
- if (!this.appwriteFolderPath) {
104
- throw new Error("Failed to get appwriteFolderPath");
102
+ ) {
103
+ const basePath = currentUserDir;
104
+
105
+ if (directConfig) {
106
+ let hasErrors = false;
107
+ if (!directConfig.appwriteEndpoint) {
108
+ console.log(chalk.red("Appwrite endpoint is required"));
109
+ hasErrors = true;
110
+ }
111
+ if (!directConfig.appwriteProject) {
112
+ console.log(chalk.red("Appwrite project is required"));
113
+ hasErrors = true;
114
+ }
115
+ if (!directConfig.appwriteKey) {
116
+ console.log(chalk.red("Appwrite key is required"));
117
+ hasErrors = true;
118
+ }
119
+ if (!hasErrors) {
120
+ // Only set config if we have all required fields
121
+ this.appwriteFolderPath = basePath;
122
+ this.config = {
123
+ appwriteEndpoint: directConfig.appwriteEndpoint!,
124
+ appwriteProject: directConfig.appwriteProject!,
125
+ appwriteKey: directConfig.appwriteKey!,
126
+ enableBackups: false,
127
+ backupInterval: 0,
128
+ backupRetention: 0,
129
+ enableBackupCleanup: false,
130
+ enableMockData: false,
131
+ documentBucketId: "",
132
+ usersCollectionName: "",
133
+ databases: [],
134
+ buckets: [],
135
+ functions: [],
136
+ };
137
+ }
138
+ } else {
139
+ // Try to find config file
140
+ const appwriteConfigFound = findAppwriteConfig(basePath);
141
+ if (!appwriteConfigFound) {
142
+ console.log(
143
+ chalk.yellow(
144
+ "No appwriteConfig.ts found and no direct configuration provided"
145
+ )
146
+ );
147
+ return;
148
+ }
149
+ this.appwriteConfigPath = appwriteConfigFound;
150
+ this.appwriteFolderPath = path.dirname(appwriteConfigFound);
105
151
  }
106
152
  }
107
153
 
108
154
  async init() {
109
155
  if (!this.config) {
110
- console.log("Initializing appwrite client & loading config...");
111
- this.config = await loadConfig(this.appwriteFolderPath);
112
- if (!this.config) {
113
- throw new Error("Failed to load config");
156
+ if (this.appwriteFolderPath && this.appwriteConfigPath) {
157
+ console.log("Loading config from file...");
158
+ this.config = await loadConfig(this.appwriteFolderPath);
159
+ if (!this.config) {
160
+ console.log(chalk.red("Failed to load config from file"));
161
+ return;
162
+ }
163
+ } else {
164
+ console.log(chalk.red("No configuration available"));
165
+ return;
114
166
  }
115
- this.appwriteServer = new Client();
116
- this.appwriteServer
117
- .setEndpoint(this.config.appwriteEndpoint)
118
- .setProject(this.config.appwriteProject)
119
- .setKey(this.config.appwriteKey);
120
- this.database = new Databases(this.appwriteServer);
121
- this.storage = new Storage(this.appwriteServer);
122
- this.config.appwriteClient = this.appwriteServer;
123
167
  }
168
+
169
+ this.appwriteServer = new Client();
170
+ this.appwriteServer
171
+ .setEndpoint(this.config.appwriteEndpoint)
172
+ .setProject(this.config.appwriteProject)
173
+ .setKey(this.config.appwriteKey);
174
+
175
+ this.database = new Databases(this.appwriteServer);
176
+ this.storage = new Storage(this.appwriteServer);
177
+ this.config.appwriteClient = this.appwriteServer;
124
178
  }
125
179
 
126
180
  async reloadConfig() {
181
+ if (!this.appwriteFolderPath) {
182
+ console.log(chalk.red("Failed to get appwriteFolderPath"));
183
+ return;
184
+ }
127
185
  this.config = await loadConfig(this.appwriteFolderPath);
186
+ if (!this.config) {
187
+ console.log(chalk.red("Failed to load config"));
188
+ return;
189
+ }
128
190
  this.appwriteServer = new Client();
129
191
  this.appwriteServer
130
192
  .setEndpoint(this.config.appwriteEndpoint)
@@ -137,14 +199,23 @@ export class UtilsController {
137
199
 
138
200
  async setupMigrationDatabase() {
139
201
  await this.init();
140
- if (!this.config) throw new Error("Config not initialized");
202
+ if (!this.config) {
203
+ console.log(chalk.red("Config not initialized"));
204
+ return;
205
+ }
141
206
  await setupMigrationDatabase(this.config);
142
207
  }
143
208
 
144
209
  async ensureDatabaseConfigBucketsExist(databases: Models.Database[] = []) {
145
210
  await this.init();
146
- if (!this.storage) throw new Error("Storage not initialized");
147
- if (!this.config) throw new Error("Config not initialized");
211
+ if (!this.storage) {
212
+ console.log(chalk.red("Storage not initialized"));
213
+ return;
214
+ }
215
+ if (!this.config) {
216
+ console.log(chalk.red("Config not initialized"));
217
+ return;
218
+ }
148
219
  await ensureDatabaseConfigBucketsExist(
149
220
  this.storage,
150
221
  this.config,
@@ -154,7 +225,10 @@ export class UtilsController {
154
225
 
155
226
  async ensureDatabasesExist(databases?: Models.Database[]) {
156
227
  await this.init();
157
- if (!this.config) throw new Error("Config not initialized");
228
+ if (!this.config) {
229
+ console.log(chalk.red("Config not initialized"));
230
+ return;
231
+ }
158
232
  await this.setupMigrationDatabase();
159
233
  await this.ensureDatabaseConfigBucketsExist(databases);
160
234
  await ensureDatabasesExist(this.config, databases);
@@ -165,13 +239,19 @@ export class UtilsController {
165
239
  collections?: Models.Collection[]
166
240
  ) {
167
241
  await this.init();
168
- if (!this.config) throw new Error("Config not initialized");
242
+ if (!this.config) {
243
+ console.log(chalk.red("Config not initialized"));
244
+ return;
245
+ }
169
246
  await ensureCollectionsExist(this.config, database, collections);
170
247
  }
171
248
 
172
249
  async getDatabasesByIds(ids: string[]) {
173
250
  await this.init();
174
- if (!this.database) throw new Error("Database not initialized");
251
+ if (!this.database) {
252
+ console.log(chalk.red("Database not initialized"));
253
+ return;
254
+ }
175
255
  if (ids.length === 0) return [];
176
256
  const dbs = await this.database.list([
177
257
  Query.limit(500),
@@ -182,22 +262,29 @@ export class UtilsController {
182
262
 
183
263
  async wipeOtherDatabases(databasesToKeep: Models.Database[]) {
184
264
  await this.init();
185
- if (!this.database) throw new Error("Database not initialized");
265
+ if (!this.database) {
266
+ console.log(chalk.red("Database not initialized"));
267
+ return;
268
+ }
186
269
  await wipeOtherDatabases(this.database, databasesToKeep);
187
270
  }
188
271
 
189
272
  async wipeUsers() {
190
273
  await this.init();
191
- if (!this.config || !this.database)
192
- throw new Error("Config or database not initialized");
274
+ if (!this.config || !this.database) {
275
+ console.log(chalk.red("Config or database not initialized"));
276
+ return;
277
+ }
193
278
  const usersController = new UsersController(this.config, this.database);
194
279
  await usersController.wipeUsers();
195
280
  }
196
281
 
197
282
  async backupDatabase(database: Models.Database) {
198
283
  await this.init();
199
- if (!this.database || !this.storage || !this.config)
200
- throw new Error("Database, storage, or config not initialized");
284
+ if (!this.database || !this.storage || !this.config) {
285
+ console.log(chalk.red("Database, storage, or config not initialized"));
286
+ return;
287
+ }
201
288
  await backupDatabase(
202
289
  this.config,
203
290
  this.database,
@@ -208,8 +295,10 @@ export class UtilsController {
208
295
 
209
296
  async listAllFunctions() {
210
297
  await this.init();
211
- if (!this.appwriteServer)
212
- throw new Error("Appwrite server not initialized");
298
+ if (!this.appwriteServer) {
299
+ console.log(chalk.red("Appwrite server not initialized"));
300
+ return [];
301
+ }
213
302
  const { functions } = await listFunctions(this.appwriteServer, [
214
303
  Query.limit(1000),
215
304
  ]);
@@ -217,8 +306,15 @@ export class UtilsController {
217
306
  }
218
307
 
219
308
  async findFunctionDirectories() {
309
+ if (!this.appwriteFolderPath) {
310
+ console.log(chalk.red("Failed to get appwriteFolderPath"));
311
+ return new Map();
312
+ }
220
313
  const functionsDir = findFunctionsDir(this.appwriteFolderPath);
221
- if (!functionsDir) return new Map();
314
+ if (!functionsDir) {
315
+ console.log(chalk.red("Failed to find functions directory"));
316
+ return new Map();
317
+ }
222
318
 
223
319
  const functionDirMap = new Map<string, string>();
224
320
  const entries = fs.readdirSync(functionsDir, { withFileTypes: true });
@@ -246,16 +342,20 @@ export class UtilsController {
246
342
  functionConfig?: AppwriteFunction
247
343
  ) {
248
344
  await this.init();
249
- if (!this.appwriteServer)
250
- throw new Error("Appwrite server not initialized");
345
+ if (!this.appwriteServer) {
346
+ console.log(chalk.red("Appwrite server not initialized"));
347
+ return;
348
+ }
251
349
 
252
350
  if (!functionConfig) {
253
351
  functionConfig = this.config?.functions?.find(
254
352
  (f) => f.name === functionName
255
353
  );
256
354
  }
257
- if (!functionConfig)
258
- throw new Error(`Function ${functionName} not found in config`);
355
+ if (!functionConfig) {
356
+ console.log(chalk.red(`Function ${functionName} not found in config`));
357
+ return;
358
+ }
259
359
 
260
360
  await deployLocalFunction(
261
361
  this.appwriteServer,
@@ -267,8 +367,10 @@ export class UtilsController {
267
367
 
268
368
  async syncFunctions() {
269
369
  await this.init();
270
- if (!this.appwriteServer)
271
- throw new Error("Appwrite server not initialized");
370
+ if (!this.appwriteServer) {
371
+ console.log(chalk.red("Appwrite server not initialized"));
372
+ return;
373
+ }
272
374
 
273
375
  const localFunctions = this.config?.functions || [];
274
376
  const remoteFunctions = await listFunctions(this.appwriteServer, [
@@ -365,15 +467,35 @@ export class UtilsController {
365
467
 
366
468
  async generateSchemas() {
367
469
  await this.init();
368
- if (!this.config) throw new Error("Config not initialized");
470
+ if (!this.config) {
471
+ console.log(chalk.red("Config not initialized"));
472
+ return;
473
+ }
474
+ if (!this.appwriteFolderPath) {
475
+ console.log(chalk.red("Failed to get appwriteFolderPath"));
476
+ return;
477
+ }
369
478
  await generateSchemas(this.config, this.appwriteFolderPath);
370
479
  }
371
480
 
372
481
  async importData(options: SetupOptions = {}) {
373
482
  await this.init();
374
- if (!this.database) throw new Error("Database not initialized");
375
- if (!this.storage) throw new Error("Storage not initialized");
376
- if (!this.config) throw new Error("Config not initialized");
483
+ if (!this.database) {
484
+ console.log(chalk.red("Database not initialized"));
485
+ return;
486
+ }
487
+ if (!this.storage) {
488
+ console.log(chalk.red("Storage not initialized"));
489
+ return;
490
+ }
491
+ if (!this.config) {
492
+ console.log(chalk.red("Config not initialized"));
493
+ return;
494
+ }
495
+ if (!this.appwriteFolderPath) {
496
+ console.log(chalk.red("Failed to get appwriteFolderPath"));
497
+ return;
498
+ }
377
499
 
378
500
  const importDataActions = new ImportDataActions(
379
501
  this.database,
@@ -401,9 +523,19 @@ export class UtilsController {
401
523
  config?: AppwriteConfig
402
524
  ) {
403
525
  await this.init();
404
- if (!this.storage) throw new Error("Storage not initialized");
526
+ if (!this.storage) {
527
+ console.log(chalk.red("Storage not initialized"));
528
+ return;
529
+ }
405
530
  const configToUse = config || this.config;
406
- if (!configToUse) throw new Error("Config not initialized");
531
+ if (!configToUse) {
532
+ console.log(chalk.red("Config not initialized"));
533
+ return;
534
+ }
535
+ if (!this.appwriteFolderPath) {
536
+ console.log(chalk.red("Failed to get appwriteFolderPath"));
537
+ return;
538
+ }
407
539
  const appwriteToX = new AppwriteToX(
408
540
  configToUse,
409
541
  this.appwriteFolderPath,
@@ -417,7 +549,10 @@ export class UtilsController {
417
549
  collections: Models.Collection[] = []
418
550
  ) {
419
551
  await this.init();
420
- if (!this.database) throw new Error("Database not initialized");
552
+ if (!this.database) {
553
+ console.log(chalk.red("Database not initialized"));
554
+ return;
555
+ }
421
556
  if (databases.length === 0) {
422
557
  const allDatabases = await fetchAllDatabases(this.database);
423
558
  databases = allDatabases;
@@ -438,7 +573,8 @@ export class UtilsController {
438
573
  let targetDatabases: Models.Database[] = [];
439
574
 
440
575
  if (!sourceClient) {
441
- throw new Error("Source database not initialized");
576
+ console.log(chalk.red("Source database not initialized"));
577
+ return;
442
578
  }
443
579
 
444
580
  if (options.isRemote) {
@@ -447,7 +583,8 @@ export class UtilsController {
447
583
  !options.transferProject ||
448
584
  !options.transferKey
449
585
  ) {
450
- throw new Error("Remote transfer options are missing");
586
+ console.log(chalk.red("Remote transfer options are missing"));
587
+ return;
451
588
  }
452
589
 
453
590
  const remoteClient = getClient(
@@ -474,7 +611,8 @@ export class UtilsController {
474
611
  );
475
612
 
476
613
  if (!fromDb || !targetDb) {
477
- throw new Error("Source or target database not found");
614
+ console.log(chalk.red("Source or target database not found"));
615
+ return;
478
616
  }
479
617
 
480
618
  if (options.isRemote && targetClient) {
@@ -503,7 +641,8 @@ export class UtilsController {
503
641
  )
504
642
  );
505
643
  } else if (!this.appwriteServer) {
506
- throw new Error("Appwrite server not initialized");
644
+ console.log(chalk.red("Appwrite server not initialized"));
645
+ return;
507
646
  } else {
508
647
  console.log(chalk.blue("Starting user transfer..."));
509
648
  const localUsers = new Users(this.appwriteServer);