appwrite-utils-cli 0.9.999 → 0.10.0
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 +1 -0
- package/dist/interactiveCLI.js +231 -89
- package/package.json +1 -1
- package/src/interactiveCLI.ts +322 -121
package/README.md
CHANGED
@@ -147,6 +147,7 @@ This updated CLI ensures that developers have robust tools at their fingertips t
|
|
147
147
|
|
148
148
|
## Changelog
|
149
149
|
|
150
|
+
- 0.10.0: Fixed `synchronize configurations` for functions, now you do not need to deploy the function first
|
150
151
|
- 0.9.999: Fixed Functions, looks for `./functions` in addition to `appwriteConfigFolder/functions`
|
151
152
|
- 0.9.998: Fixed transfer finally, added `--targetDbId` and `--sourceDbId` as aliases
|
152
153
|
- 0.9.994: Added function deployment management, in BETA, and fixed document transfer between databases
|
package/dist/interactiveCLI.js
CHANGED
@@ -10,7 +10,7 @@ import { AppwriteFunctionSchema, parseAttribute, PermissionToAppwritePermission,
|
|
10
10
|
import { ulid } from "ulidx";
|
11
11
|
import chalk from "chalk";
|
12
12
|
import { DateTime } from "luxon";
|
13
|
-
import { createFunctionTemplate, deleteFunction, downloadLatestFunctionDeployment, listFunctions, listSpecifications, } from "./functions/methods.js";
|
13
|
+
import { createFunctionTemplate, deleteFunction, downloadLatestFunctionDeployment, getFunction, listFunctions, listSpecifications, } from "./functions/methods.js";
|
14
14
|
import { deployLocalFunction } from "./functions/deployments.js";
|
15
15
|
import { join } from "node:path";
|
16
16
|
import fs from "node:fs";
|
@@ -361,83 +361,127 @@ export class InteractiveCLI {
|
|
361
361
|
console.log(chalk.red("Invalid function configuration"));
|
362
362
|
return;
|
363
363
|
}
|
364
|
-
|
365
|
-
|
366
|
-
|
367
|
-
|
368
|
-
|
369
|
-
|
370
|
-
|
371
|
-
|
364
|
+
const { shouldDownload } = await inquirer.prompt([
|
365
|
+
{
|
366
|
+
type: "confirm",
|
367
|
+
name: "shouldDownload",
|
368
|
+
message: "Would you like to download the latest deployment?",
|
369
|
+
default: false,
|
370
|
+
},
|
371
|
+
]);
|
372
|
+
if (shouldDownload) {
|
373
|
+
try {
|
374
|
+
console.log(chalk.blue("Downloading latest deployment..."));
|
375
|
+
const { path: downloadedPath, function: remoteFunction } = await downloadLatestFunctionDeployment(this.controller.appwriteServer, functionConfig.$id, join(this.controller.getAppwriteFolderPath(), "functions"));
|
376
|
+
console.log(chalk.green(`✨ Function downloaded to ${downloadedPath}`));
|
377
|
+
// Update the config and functions array safely
|
378
|
+
this.controller.config.functions =
|
379
|
+
this.controller.config.functions || [];
|
380
|
+
const newFunction = {
|
381
|
+
$id: remoteFunction.$id,
|
382
|
+
name: remoteFunction.name,
|
383
|
+
runtime: remoteFunction.runtime,
|
384
|
+
execute: remoteFunction.execute || [],
|
385
|
+
events: remoteFunction.events || [],
|
386
|
+
schedule: remoteFunction.schedule || "",
|
387
|
+
timeout: remoteFunction.timeout || 15,
|
388
|
+
enabled: remoteFunction.enabled !== false,
|
389
|
+
logging: remoteFunction.logging !== false,
|
390
|
+
entrypoint: remoteFunction.entrypoint || "src/index.ts",
|
391
|
+
commands: remoteFunction.commands || "npm install",
|
392
|
+
dirPath: downloadedPath,
|
393
|
+
scopes: (remoteFunction.scopes || []),
|
394
|
+
installationId: remoteFunction.installationId,
|
395
|
+
providerRepositoryId: remoteFunction.providerRepositoryId,
|
396
|
+
providerBranch: remoteFunction.providerBranch,
|
397
|
+
providerSilentMode: remoteFunction.providerSilentMode,
|
398
|
+
providerRootDirectory: remoteFunction.providerRootDirectory,
|
399
|
+
specification: remoteFunction.specification,
|
400
|
+
};
|
401
|
+
const existingIndex = this.controller.config.functions.findIndex((f) => f?.$id === remoteFunction.$id);
|
402
|
+
if (existingIndex >= 0) {
|
403
|
+
this.controller.config.functions[existingIndex] = newFunction;
|
404
|
+
}
|
405
|
+
else {
|
406
|
+
this.controller.config.functions.push(newFunction);
|
407
|
+
}
|
408
|
+
const schemaGenerator = new SchemaGenerator(this.controller.config, this.controller.getAppwriteFolderPath());
|
409
|
+
schemaGenerator.updateConfig(this.controller.config);
|
410
|
+
console.log(chalk.green("✨ Updated appwriteConfig.ts with new function"));
|
411
|
+
await this.controller.reloadConfig();
|
412
|
+
functionConfig.dirPath = downloadedPath;
|
372
413
|
}
|
373
|
-
|
374
|
-
console.
|
375
|
-
|
376
|
-
|
377
|
-
|
378
|
-
|
379
|
-
|
380
|
-
|
381
|
-
|
382
|
-
|
383
|
-
|
384
|
-
|
385
|
-
|
386
|
-
|
387
|
-
|
388
|
-
|
389
|
-
|
390
|
-
|
391
|
-
|
392
|
-
|
393
|
-
|
394
|
-
|
395
|
-
|
396
|
-
|
397
|
-
|
398
|
-
|
399
|
-
|
400
|
-
|
401
|
-
|
402
|
-
|
403
|
-
|
404
|
-
|
405
|
-
|
406
|
-
|
407
|
-
|
408
|
-
providerSilentMode: remoteFunction.providerSilentMode,
|
409
|
-
providerRootDirectory: remoteFunction.providerRootDirectory,
|
410
|
-
specification: remoteFunction.specification,
|
411
|
-
};
|
412
|
-
const existingIndex = this.controller.config.functions.findIndex((f) => f?.$id === remoteFunction.$id);
|
413
|
-
if (existingIndex >= 0) {
|
414
|
-
this.controller.config.functions[existingIndex] = newFunction;
|
415
|
-
}
|
416
|
-
else {
|
417
|
-
this.controller.config.functions.push(newFunction);
|
418
|
-
}
|
419
|
-
const schemaGenerator = new SchemaGenerator(this.controller.config, this.controller.getAppwriteFolderPath());
|
420
|
-
schemaGenerator.updateConfig(this.controller.config);
|
421
|
-
console.log(chalk.green("✨ Updated appwriteConfig.ts with new function"));
|
422
|
-
await this.controller.reloadConfig();
|
423
|
-
functionConfig.dirPath = downloadedPath;
|
424
|
-
}
|
425
|
-
catch (error) {
|
426
|
-
console.error(chalk.red("Failed to download function deployment:"), error);
|
427
|
-
return;
|
428
|
-
}
|
414
|
+
catch (error) {
|
415
|
+
console.error(chalk.red("Failed to download function deployment:"), error);
|
416
|
+
return;
|
417
|
+
}
|
418
|
+
}
|
419
|
+
else {
|
420
|
+
console.log(chalk.yellow("Skipping download, updating config only..."));
|
421
|
+
try {
|
422
|
+
// Update the config with remote function info
|
423
|
+
this.controller.config.functions =
|
424
|
+
this.controller.config.functions || [];
|
425
|
+
const remoteFunction = await getFunction(this.controller.appwriteServer, functionConfig.$id);
|
426
|
+
const newFunction = {
|
427
|
+
$id: remoteFunction.$id,
|
428
|
+
name: remoteFunction.name,
|
429
|
+
runtime: remoteFunction.runtime,
|
430
|
+
execute: remoteFunction.execute || [],
|
431
|
+
events: remoteFunction.events || [],
|
432
|
+
schedule: remoteFunction.schedule || "",
|
433
|
+
timeout: remoteFunction.timeout || 15,
|
434
|
+
enabled: remoteFunction.enabled !== false,
|
435
|
+
logging: remoteFunction.logging !== false,
|
436
|
+
entrypoint: remoteFunction.entrypoint || "src/index.ts",
|
437
|
+
commands: remoteFunction.commands || "npm install",
|
438
|
+
scopes: (remoteFunction.scopes || []),
|
439
|
+
installationId: remoteFunction.installationId,
|
440
|
+
providerRepositoryId: remoteFunction.providerRepositoryId,
|
441
|
+
providerBranch: remoteFunction.providerBranch,
|
442
|
+
providerSilentMode: remoteFunction.providerSilentMode,
|
443
|
+
providerRootDirectory: remoteFunction.providerRootDirectory,
|
444
|
+
specification: remoteFunction.specification,
|
445
|
+
};
|
446
|
+
const existingIndex = this.controller.config.functions.findIndex((f) => f?.$id === remoteFunction.$id);
|
447
|
+
if (existingIndex >= 0) {
|
448
|
+
this.controller.config.functions[existingIndex] = newFunction;
|
429
449
|
}
|
430
450
|
else {
|
431
|
-
|
432
|
-
return;
|
451
|
+
this.controller.config.functions.push(newFunction);
|
433
452
|
}
|
453
|
+
const schemaGenerator = new SchemaGenerator(this.controller.config, this.controller.getAppwriteFolderPath());
|
454
|
+
schemaGenerator.updateConfig(this.controller.config);
|
455
|
+
console.log(chalk.green("✨ Updated appwriteConfig.ts with function info"));
|
456
|
+
await this.controller.reloadConfig();
|
457
|
+
}
|
458
|
+
catch (error) {
|
459
|
+
console.error(chalk.red("Failed to update function config:"), error);
|
460
|
+
return;
|
434
461
|
}
|
435
462
|
}
|
436
|
-
if
|
437
|
-
|
438
|
-
|
463
|
+
// Only try to deploy if we didn't just download
|
464
|
+
if (!shouldDownload) {
|
465
|
+
let functionPath = join(this.controller.getAppwriteFolderPath(), "functions", functionConfig.name);
|
466
|
+
if (!fs.existsSync(functionPath)) {
|
467
|
+
console.log(chalk.yellow(`Function not found in primary location, searching subdirectories...`));
|
468
|
+
const foundPath = await this.findFunctionInSubdirectories(this.controller.getAppwriteFolderPath(), functionConfig.name);
|
469
|
+
if (foundPath) {
|
470
|
+
console.log(chalk.green(`Found function at: ${foundPath}`));
|
471
|
+
functionPath = foundPath;
|
472
|
+
functionConfig.dirPath = foundPath;
|
473
|
+
}
|
474
|
+
else {
|
475
|
+
console.log(chalk.red(`Function ${functionConfig.name} not found locally in any subdirectory. Cannot deploy.`));
|
476
|
+
return;
|
477
|
+
}
|
478
|
+
}
|
479
|
+
if (!this.controller.appwriteServer) {
|
480
|
+
console.log(chalk.red("Appwrite server not initialized"));
|
481
|
+
return;
|
482
|
+
}
|
483
|
+
await deployLocalFunction(this.controller.appwriteServer, functionConfig.name, functionConfig);
|
439
484
|
}
|
440
|
-
await deployLocalFunction(this.controller.appwriteServer, functionConfig.name, functionConfig);
|
441
485
|
}
|
442
486
|
async deleteFunction() {
|
443
487
|
const functions = await this.selectFunctions("Select functions to delete:", true, false);
|
@@ -805,55 +849,153 @@ export class InteractiveCLI {
|
|
805
849
|
const hasLocal = localFunctions.some((lf) => lf.$id === func.$id);
|
806
850
|
const hasRemote = remoteFunctions.some((rf) => rf.$id === func.$id);
|
807
851
|
if (hasLocal && hasRemote) {
|
852
|
+
// First try to find the function locally
|
853
|
+
let functionPath = join(this.controller.getAppwriteFolderPath(), "functions", func.name);
|
854
|
+
if (!fs.existsSync(functionPath)) {
|
855
|
+
console.log(chalk.yellow(`Function not found in primary location, searching subdirectories...`));
|
856
|
+
const foundPath = await this.findFunctionInSubdirectories(this.controller.getAppwriteFolderPath(), func.name);
|
857
|
+
if (foundPath) {
|
858
|
+
console.log(chalk.green(`Found function at: ${foundPath}`));
|
859
|
+
functionPath = foundPath;
|
860
|
+
}
|
861
|
+
}
|
808
862
|
const { preference } = await inquirer.prompt([
|
809
863
|
{
|
810
864
|
type: "list",
|
811
865
|
name: "preference",
|
812
|
-
message: `Function "${func.name}"
|
866
|
+
message: `Function "${func.name}" ${functionPath ? "found at " + functionPath : "not found locally"}. What would you like to do?`,
|
813
867
|
choices: [
|
814
|
-
|
815
|
-
|
816
|
-
|
817
|
-
|
868
|
+
...(functionPath
|
869
|
+
? [
|
870
|
+
{
|
871
|
+
name: "Keep local version (deploy to remote)",
|
872
|
+
value: "local",
|
873
|
+
},
|
874
|
+
]
|
875
|
+
: []),
|
818
876
|
{ name: "Use remote version (download)", value: "remote" },
|
877
|
+
{ name: "Update config only", value: "config" },
|
819
878
|
{ name: "Skip this function", value: "skip" },
|
820
879
|
],
|
821
880
|
},
|
822
881
|
]);
|
823
|
-
if (preference === "local") {
|
882
|
+
if (preference === "local" && functionPath) {
|
824
883
|
await this.controller.deployFunction(func.name);
|
825
884
|
}
|
826
885
|
else if (preference === "remote") {
|
827
886
|
await downloadLatestFunctionDeployment(this.controller.appwriteServer, func.$id, join(this.controller.getAppwriteFolderPath(), "functions"));
|
828
887
|
}
|
888
|
+
else if (preference === "config") {
|
889
|
+
const remoteFunction = await getFunction(this.controller.appwriteServer, func.$id);
|
890
|
+
const newFunction = {
|
891
|
+
$id: remoteFunction.$id,
|
892
|
+
name: remoteFunction.name,
|
893
|
+
runtime: remoteFunction.runtime,
|
894
|
+
execute: remoteFunction.execute || [],
|
895
|
+
events: remoteFunction.events || [],
|
896
|
+
schedule: remoteFunction.schedule || "",
|
897
|
+
timeout: remoteFunction.timeout || 15,
|
898
|
+
enabled: remoteFunction.enabled !== false,
|
899
|
+
logging: remoteFunction.logging !== false,
|
900
|
+
entrypoint: remoteFunction.entrypoint || "src/index.ts",
|
901
|
+
commands: remoteFunction.commands || "npm install",
|
902
|
+
scopes: (remoteFunction.scopes || []),
|
903
|
+
installationId: remoteFunction.installationId,
|
904
|
+
providerRepositoryId: remoteFunction.providerRepositoryId,
|
905
|
+
providerBranch: remoteFunction.providerBranch,
|
906
|
+
providerSilentMode: remoteFunction.providerSilentMode,
|
907
|
+
providerRootDirectory: remoteFunction.providerRootDirectory,
|
908
|
+
specification: remoteFunction.specification,
|
909
|
+
};
|
910
|
+
const existingIndex = this.controller.config.functions.findIndex((f) => f.$id === remoteFunction.$id);
|
911
|
+
if (existingIndex >= 0) {
|
912
|
+
this.controller.config.functions[existingIndex] = newFunction;
|
913
|
+
}
|
914
|
+
else {
|
915
|
+
this.controller.config.functions.push(newFunction);
|
916
|
+
}
|
917
|
+
console.log(chalk.green(`Updated config for function: ${func.name}`));
|
918
|
+
}
|
829
919
|
}
|
830
920
|
else if (hasLocal) {
|
831
|
-
|
921
|
+
// Similar check for local-only functions
|
922
|
+
let functionPath = join(this.controller.getAppwriteFolderPath(), "functions", func.name);
|
923
|
+
if (!fs.existsSync(functionPath)) {
|
924
|
+
const foundPath = await this.findFunctionInSubdirectories(this.controller.getAppwriteFolderPath(), func.name);
|
925
|
+
if (foundPath) {
|
926
|
+
functionPath = foundPath;
|
927
|
+
}
|
928
|
+
}
|
929
|
+
const { action } = await inquirer.prompt([
|
832
930
|
{
|
833
|
-
type: "
|
834
|
-
name: "
|
835
|
-
message: `Function "${func.name}"
|
836
|
-
|
931
|
+
type: "list",
|
932
|
+
name: "action",
|
933
|
+
message: `Function "${func.name}" ${functionPath ? "found at " + functionPath : "not found locally"}. What would you like to do?`,
|
934
|
+
choices: [
|
935
|
+
...(functionPath
|
936
|
+
? [
|
937
|
+
{
|
938
|
+
name: "Deploy to remote",
|
939
|
+
value: "deploy",
|
940
|
+
},
|
941
|
+
]
|
942
|
+
: []),
|
943
|
+
{ name: "Skip this function", value: "skip" },
|
944
|
+
],
|
837
945
|
},
|
838
946
|
]);
|
839
|
-
if (deploy) {
|
947
|
+
if (action === "deploy" && functionPath) {
|
840
948
|
await this.controller.deployFunction(func.name);
|
841
949
|
}
|
842
950
|
}
|
843
951
|
else if (hasRemote) {
|
844
|
-
const {
|
952
|
+
const { action } = await inquirer.prompt([
|
845
953
|
{
|
846
|
-
type: "
|
847
|
-
name: "
|
848
|
-
message: `Function "${func.name}" exists only remotely.
|
849
|
-
|
954
|
+
type: "list",
|
955
|
+
name: "action",
|
956
|
+
message: `Function "${func.name}" exists only remotely. What would you like to do?`,
|
957
|
+
choices: [
|
958
|
+
{ name: "Update config only", value: "config" },
|
959
|
+
{ name: "Download locally", value: "download" },
|
960
|
+
{ name: "Skip this function", value: "skip" },
|
961
|
+
],
|
850
962
|
},
|
851
963
|
]);
|
852
|
-
if (download) {
|
964
|
+
if (action === "download") {
|
853
965
|
await downloadLatestFunctionDeployment(this.controller.appwriteServer, func.$id, join(this.controller.getAppwriteFolderPath(), "functions"));
|
854
966
|
}
|
967
|
+
else if (action === "config") {
|
968
|
+
const remoteFunction = await getFunction(this.controller.appwriteServer, func.$id);
|
969
|
+
const newFunction = {
|
970
|
+
$id: remoteFunction.$id,
|
971
|
+
name: remoteFunction.name,
|
972
|
+
runtime: remoteFunction.runtime,
|
973
|
+
execute: remoteFunction.execute || [],
|
974
|
+
events: remoteFunction.events || [],
|
975
|
+
schedule: remoteFunction.schedule || "",
|
976
|
+
timeout: remoteFunction.timeout || 15,
|
977
|
+
enabled: remoteFunction.enabled !== false,
|
978
|
+
logging: remoteFunction.logging !== false,
|
979
|
+
entrypoint: remoteFunction.entrypoint || "src/index.ts",
|
980
|
+
commands: remoteFunction.commands || "npm install",
|
981
|
+
scopes: (remoteFunction.scopes || []),
|
982
|
+
installationId: remoteFunction.installationId,
|
983
|
+
providerRepositoryId: remoteFunction.providerRepositoryId,
|
984
|
+
providerBranch: remoteFunction.providerBranch,
|
985
|
+
providerSilentMode: remoteFunction.providerSilentMode,
|
986
|
+
providerRootDirectory: remoteFunction.providerRootDirectory,
|
987
|
+
specification: remoteFunction.specification,
|
988
|
+
};
|
989
|
+
this.controller.config.functions =
|
990
|
+
this.controller.config.functions || [];
|
991
|
+
this.controller.config.functions.push(newFunction);
|
992
|
+
console.log(chalk.green(`Added config for remote function: ${func.name}`));
|
993
|
+
}
|
855
994
|
}
|
856
995
|
}
|
996
|
+
// Update schemas after all changes
|
997
|
+
const schemaGenerator = new SchemaGenerator(this.controller.config, this.controller.getAppwriteFolderPath());
|
998
|
+
schemaGenerator.updateConfig(this.controller.config);
|
857
999
|
}
|
858
1000
|
console.log(chalk.green("✨ Configurations synchronized successfully!"));
|
859
1001
|
}
|
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.
|
4
|
+
"version": "0.10.0",
|
5
5
|
"main": "src/main.ts",
|
6
6
|
"type": "module",
|
7
7
|
"repository": {
|
package/src/interactiveCLI.ts
CHANGED
@@ -35,6 +35,7 @@ import {
|
|
35
35
|
createFunctionTemplate,
|
36
36
|
deleteFunction,
|
37
37
|
downloadLatestFunctionDeployment,
|
38
|
+
getFunction,
|
38
39
|
listFunctions,
|
39
40
|
listSpecifications,
|
40
41
|
} from "./functions/methods.js";
|
@@ -486,127 +487,183 @@ export class InteractiveCLI {
|
|
486
487
|
return;
|
487
488
|
}
|
488
489
|
|
489
|
-
|
490
|
-
|
491
|
-
|
492
|
-
|
493
|
-
|
490
|
+
const { shouldDownload } = await inquirer.prompt([
|
491
|
+
{
|
492
|
+
type: "confirm",
|
493
|
+
name: "shouldDownload",
|
494
|
+
message: "Would you like to download the latest deployment?",
|
495
|
+
default: false,
|
496
|
+
},
|
497
|
+
]);
|
494
498
|
|
495
|
-
if (
|
496
|
-
|
497
|
-
chalk.
|
498
|
-
|
499
|
-
|
500
|
-
|
501
|
-
|
502
|
-
|
503
|
-
|
504
|
-
|
499
|
+
if (shouldDownload) {
|
500
|
+
try {
|
501
|
+
console.log(chalk.blue("Downloading latest deployment..."));
|
502
|
+
const { path: downloadedPath, function: remoteFunction } =
|
503
|
+
await downloadLatestFunctionDeployment(
|
504
|
+
this.controller.appwriteServer!,
|
505
|
+
functionConfig.$id,
|
506
|
+
join(this.controller.getAppwriteFolderPath(), "functions")
|
507
|
+
);
|
508
|
+
console.log(chalk.green(`✨ Function downloaded to ${downloadedPath}`));
|
509
|
+
|
510
|
+
// Update the config and functions array safely
|
511
|
+
this.controller.config.functions =
|
512
|
+
this.controller.config.functions || [];
|
513
|
+
|
514
|
+
const newFunction = {
|
515
|
+
$id: remoteFunction.$id,
|
516
|
+
name: remoteFunction.name,
|
517
|
+
runtime: remoteFunction.runtime as Runtime,
|
518
|
+
execute: remoteFunction.execute || [],
|
519
|
+
events: remoteFunction.events || [],
|
520
|
+
schedule: remoteFunction.schedule || "",
|
521
|
+
timeout: remoteFunction.timeout || 15,
|
522
|
+
enabled: remoteFunction.enabled !== false,
|
523
|
+
logging: remoteFunction.logging !== false,
|
524
|
+
entrypoint: remoteFunction.entrypoint || "src/index.ts",
|
525
|
+
commands: remoteFunction.commands || "npm install",
|
526
|
+
dirPath: downloadedPath,
|
527
|
+
scopes: (remoteFunction.scopes || []) as FunctionScope[],
|
528
|
+
installationId: remoteFunction.installationId,
|
529
|
+
providerRepositoryId: remoteFunction.providerRepositoryId,
|
530
|
+
providerBranch: remoteFunction.providerBranch,
|
531
|
+
providerSilentMode: remoteFunction.providerSilentMode,
|
532
|
+
providerRootDirectory: remoteFunction.providerRootDirectory,
|
533
|
+
specification: remoteFunction.specification as Specification,
|
534
|
+
};
|
535
|
+
|
536
|
+
const existingIndex = this.controller.config.functions.findIndex(
|
537
|
+
(f) => f?.$id === remoteFunction.$id
|
538
|
+
);
|
505
539
|
|
506
|
-
|
507
|
-
|
508
|
-
|
509
|
-
|
510
|
-
|
540
|
+
if (existingIndex >= 0) {
|
541
|
+
this.controller.config.functions[existingIndex] = newFunction;
|
542
|
+
} else {
|
543
|
+
this.controller.config.functions.push(newFunction);
|
544
|
+
}
|
545
|
+
|
546
|
+
const schemaGenerator = new SchemaGenerator(
|
547
|
+
this.controller.config,
|
548
|
+
this.controller.getAppwriteFolderPath()
|
549
|
+
);
|
550
|
+
schemaGenerator.updateConfig(this.controller.config);
|
511
551
|
console.log(
|
512
|
-
chalk.
|
513
|
-
`Function ${functionConfig.name} not found locally in any subdirectory`
|
514
|
-
)
|
552
|
+
chalk.green("✨ Updated appwriteConfig.ts with new function")
|
515
553
|
);
|
516
554
|
|
517
|
-
|
518
|
-
|
519
|
-
|
520
|
-
|
521
|
-
|
522
|
-
|
523
|
-
|
524
|
-
|
555
|
+
await this.controller.reloadConfig();
|
556
|
+
functionConfig.dirPath = downloadedPath;
|
557
|
+
} catch (error) {
|
558
|
+
console.error(
|
559
|
+
chalk.red("Failed to download function deployment:"),
|
560
|
+
error
|
561
|
+
);
|
562
|
+
return;
|
563
|
+
}
|
564
|
+
} else {
|
565
|
+
console.log(chalk.yellow("Skipping download, updating config only..."));
|
566
|
+
try {
|
567
|
+
// Update the config with remote function info
|
568
|
+
this.controller.config.functions =
|
569
|
+
this.controller.config.functions || [];
|
525
570
|
|
526
|
-
|
527
|
-
|
528
|
-
|
529
|
-
|
530
|
-
await downloadLatestFunctionDeployment(
|
531
|
-
this.controller.appwriteServer!,
|
532
|
-
functionConfig.$id,
|
533
|
-
join(this.controller.getAppwriteFolderPath(), "functions")
|
534
|
-
);
|
535
|
-
console.log(
|
536
|
-
chalk.green(`✨ Function downloaded to ${downloadedPath}`)
|
537
|
-
);
|
571
|
+
const remoteFunction = await getFunction(
|
572
|
+
this.controller.appwriteServer!,
|
573
|
+
functionConfig.$id
|
574
|
+
);
|
538
575
|
|
539
|
-
|
540
|
-
|
541
|
-
|
576
|
+
const newFunction = {
|
577
|
+
$id: remoteFunction.$id,
|
578
|
+
name: remoteFunction.name,
|
579
|
+
runtime: remoteFunction.runtime as Runtime,
|
580
|
+
execute: remoteFunction.execute || [],
|
581
|
+
events: remoteFunction.events || [],
|
582
|
+
schedule: remoteFunction.schedule || "",
|
583
|
+
timeout: remoteFunction.timeout || 15,
|
584
|
+
enabled: remoteFunction.enabled !== false,
|
585
|
+
logging: remoteFunction.logging !== false,
|
586
|
+
entrypoint: remoteFunction.entrypoint || "src/index.ts",
|
587
|
+
commands: remoteFunction.commands || "npm install",
|
588
|
+
scopes: (remoteFunction.scopes || []) as FunctionScope[],
|
589
|
+
installationId: remoteFunction.installationId,
|
590
|
+
providerRepositoryId: remoteFunction.providerRepositoryId,
|
591
|
+
providerBranch: remoteFunction.providerBranch,
|
592
|
+
providerSilentMode: remoteFunction.providerSilentMode,
|
593
|
+
providerRootDirectory: remoteFunction.providerRootDirectory,
|
594
|
+
specification: remoteFunction.specification as Specification,
|
595
|
+
};
|
596
|
+
|
597
|
+
const existingIndex = this.controller.config.functions.findIndex(
|
598
|
+
(f) => f?.$id === remoteFunction.$id
|
599
|
+
);
|
542
600
|
|
543
|
-
|
544
|
-
|
545
|
-
|
546
|
-
|
547
|
-
|
548
|
-
events: remoteFunction.events || [],
|
549
|
-
schedule: remoteFunction.schedule || "",
|
550
|
-
timeout: remoteFunction.timeout || 15,
|
551
|
-
enabled: remoteFunction.enabled !== false,
|
552
|
-
logging: remoteFunction.logging !== false,
|
553
|
-
entrypoint: remoteFunction.entrypoint || "src/index.ts",
|
554
|
-
commands: remoteFunction.commands || "npm install",
|
555
|
-
dirPath: downloadedPath,
|
556
|
-
scopes: (remoteFunction.scopes || []) as FunctionScope[],
|
557
|
-
installationId: remoteFunction.installationId,
|
558
|
-
providerRepositoryId: remoteFunction.providerRepositoryId,
|
559
|
-
providerBranch: remoteFunction.providerBranch,
|
560
|
-
providerSilentMode: remoteFunction.providerSilentMode,
|
561
|
-
providerRootDirectory: remoteFunction.providerRootDirectory,
|
562
|
-
specification: remoteFunction.specification as Specification,
|
563
|
-
};
|
601
|
+
if (existingIndex >= 0) {
|
602
|
+
this.controller.config.functions[existingIndex] = newFunction;
|
603
|
+
} else {
|
604
|
+
this.controller.config.functions.push(newFunction);
|
605
|
+
}
|
564
606
|
|
565
|
-
|
566
|
-
|
567
|
-
|
607
|
+
const schemaGenerator = new SchemaGenerator(
|
608
|
+
this.controller.config,
|
609
|
+
this.controller.getAppwriteFolderPath()
|
610
|
+
);
|
611
|
+
schemaGenerator.updateConfig(this.controller.config);
|
612
|
+
console.log(
|
613
|
+
chalk.green("✨ Updated appwriteConfig.ts with function info")
|
614
|
+
);
|
568
615
|
|
569
|
-
|
570
|
-
|
571
|
-
|
572
|
-
|
573
|
-
|
616
|
+
await this.controller.reloadConfig();
|
617
|
+
} catch (error) {
|
618
|
+
console.error(chalk.red("Failed to update function config:"), error);
|
619
|
+
return;
|
620
|
+
}
|
621
|
+
}
|
574
622
|
|
575
|
-
|
576
|
-
|
577
|
-
|
578
|
-
|
579
|
-
|
580
|
-
|
581
|
-
|
582
|
-
);
|
623
|
+
// Only try to deploy if we didn't just download
|
624
|
+
if (!shouldDownload) {
|
625
|
+
let functionPath = join(
|
626
|
+
this.controller.getAppwriteFolderPath(),
|
627
|
+
"functions",
|
628
|
+
functionConfig.name
|
629
|
+
);
|
583
630
|
|
584
|
-
|
585
|
-
|
586
|
-
|
587
|
-
|
588
|
-
|
589
|
-
|
590
|
-
|
591
|
-
|
592
|
-
|
631
|
+
if (!fs.existsSync(functionPath)) {
|
632
|
+
console.log(
|
633
|
+
chalk.yellow(
|
634
|
+
`Function not found in primary location, searching subdirectories...`
|
635
|
+
)
|
636
|
+
);
|
637
|
+
const foundPath = await this.findFunctionInSubdirectories(
|
638
|
+
this.controller.getAppwriteFolderPath(),
|
639
|
+
functionConfig.name
|
640
|
+
);
|
641
|
+
|
642
|
+
if (foundPath) {
|
643
|
+
console.log(chalk.green(`Found function at: ${foundPath}`));
|
644
|
+
functionPath = foundPath;
|
645
|
+
functionConfig.dirPath = foundPath;
|
593
646
|
} else {
|
594
|
-
console.log(
|
647
|
+
console.log(
|
648
|
+
chalk.red(
|
649
|
+
`Function ${functionConfig.name} not found locally in any subdirectory. Cannot deploy.`
|
650
|
+
)
|
651
|
+
);
|
595
652
|
return;
|
596
653
|
}
|
597
654
|
}
|
598
|
-
}
|
599
655
|
|
600
|
-
|
601
|
-
|
602
|
-
|
603
|
-
|
656
|
+
if (!this.controller.appwriteServer) {
|
657
|
+
console.log(chalk.red("Appwrite server not initialized"));
|
658
|
+
return;
|
659
|
+
}
|
604
660
|
|
605
|
-
|
606
|
-
|
607
|
-
|
608
|
-
|
609
|
-
|
661
|
+
await deployLocalFunction(
|
662
|
+
this.controller.appwriteServer,
|
663
|
+
functionConfig.name,
|
664
|
+
functionConfig
|
665
|
+
);
|
666
|
+
}
|
610
667
|
}
|
611
668
|
|
612
669
|
private async deleteFunction(): Promise<void> {
|
@@ -1102,23 +1159,54 @@ export class InteractiveCLI {
|
|
1102
1159
|
const hasRemote = remoteFunctions.some((rf) => rf.$id === func.$id);
|
1103
1160
|
|
1104
1161
|
if (hasLocal && hasRemote) {
|
1162
|
+
// First try to find the function locally
|
1163
|
+
let functionPath = join(
|
1164
|
+
this.controller!.getAppwriteFolderPath(),
|
1165
|
+
"functions",
|
1166
|
+
func.name
|
1167
|
+
);
|
1168
|
+
|
1169
|
+
if (!fs.existsSync(functionPath)) {
|
1170
|
+
console.log(
|
1171
|
+
chalk.yellow(
|
1172
|
+
`Function not found in primary location, searching subdirectories...`
|
1173
|
+
)
|
1174
|
+
);
|
1175
|
+
const foundPath = await this.findFunctionInSubdirectories(
|
1176
|
+
this.controller!.getAppwriteFolderPath(),
|
1177
|
+
func.name
|
1178
|
+
);
|
1179
|
+
|
1180
|
+
if (foundPath) {
|
1181
|
+
console.log(chalk.green(`Found function at: ${foundPath}`));
|
1182
|
+
functionPath = foundPath;
|
1183
|
+
}
|
1184
|
+
}
|
1185
|
+
|
1105
1186
|
const { preference } = await inquirer.prompt([
|
1106
1187
|
{
|
1107
1188
|
type: "list",
|
1108
1189
|
name: "preference",
|
1109
|
-
message: `Function "${func.name}"
|
1190
|
+
message: `Function "${func.name}" ${
|
1191
|
+
functionPath ? "found at " + functionPath : "not found locally"
|
1192
|
+
}. What would you like to do?`,
|
1110
1193
|
choices: [
|
1111
|
-
|
1112
|
-
|
1113
|
-
|
1114
|
-
|
1194
|
+
...(functionPath
|
1195
|
+
? [
|
1196
|
+
{
|
1197
|
+
name: "Keep local version (deploy to remote)",
|
1198
|
+
value: "local",
|
1199
|
+
},
|
1200
|
+
]
|
1201
|
+
: []),
|
1115
1202
|
{ name: "Use remote version (download)", value: "remote" },
|
1203
|
+
{ name: "Update config only", value: "config" },
|
1116
1204
|
{ name: "Skip this function", value: "skip" },
|
1117
1205
|
],
|
1118
1206
|
},
|
1119
1207
|
]);
|
1120
1208
|
|
1121
|
-
if (preference === "local") {
|
1209
|
+
if (preference === "local" && functionPath) {
|
1122
1210
|
await this.controller!.deployFunction(func.name);
|
1123
1211
|
} else if (preference === "remote") {
|
1124
1212
|
await downloadLatestFunctionDeployment(
|
@@ -1126,39 +1214,152 @@ export class InteractiveCLI {
|
|
1126
1214
|
func.$id,
|
1127
1215
|
join(this.controller!.getAppwriteFolderPath(), "functions")
|
1128
1216
|
);
|
1217
|
+
} else if (preference === "config") {
|
1218
|
+
const remoteFunction = await getFunction(
|
1219
|
+
this.controller!.appwriteServer!,
|
1220
|
+
func.$id
|
1221
|
+
);
|
1222
|
+
|
1223
|
+
const newFunction = {
|
1224
|
+
$id: remoteFunction.$id,
|
1225
|
+
name: remoteFunction.name,
|
1226
|
+
runtime: remoteFunction.runtime as Runtime,
|
1227
|
+
execute: remoteFunction.execute || [],
|
1228
|
+
events: remoteFunction.events || [],
|
1229
|
+
schedule: remoteFunction.schedule || "",
|
1230
|
+
timeout: remoteFunction.timeout || 15,
|
1231
|
+
enabled: remoteFunction.enabled !== false,
|
1232
|
+
logging: remoteFunction.logging !== false,
|
1233
|
+
entrypoint: remoteFunction.entrypoint || "src/index.ts",
|
1234
|
+
commands: remoteFunction.commands || "npm install",
|
1235
|
+
scopes: (remoteFunction.scopes || []) as FunctionScope[],
|
1236
|
+
installationId: remoteFunction.installationId,
|
1237
|
+
providerRepositoryId: remoteFunction.providerRepositoryId,
|
1238
|
+
providerBranch: remoteFunction.providerBranch,
|
1239
|
+
providerSilentMode: remoteFunction.providerSilentMode,
|
1240
|
+
providerRootDirectory: remoteFunction.providerRootDirectory,
|
1241
|
+
specification: remoteFunction.specification as Specification,
|
1242
|
+
};
|
1243
|
+
|
1244
|
+
const existingIndex = this.controller!.config!.functions!.findIndex(
|
1245
|
+
(f) => f.$id === remoteFunction.$id
|
1246
|
+
);
|
1247
|
+
|
1248
|
+
if (existingIndex >= 0) {
|
1249
|
+
this.controller!.config!.functions![existingIndex] = newFunction;
|
1250
|
+
} else {
|
1251
|
+
this.controller!.config!.functions!.push(newFunction);
|
1252
|
+
}
|
1253
|
+
console.log(
|
1254
|
+
chalk.green(`Updated config for function: ${func.name}`)
|
1255
|
+
);
|
1129
1256
|
}
|
1130
1257
|
} else if (hasLocal) {
|
1131
|
-
|
1258
|
+
// Similar check for local-only functions
|
1259
|
+
let functionPath = join(
|
1260
|
+
this.controller!.getAppwriteFolderPath(),
|
1261
|
+
"functions",
|
1262
|
+
func.name
|
1263
|
+
);
|
1264
|
+
|
1265
|
+
if (!fs.existsSync(functionPath)) {
|
1266
|
+
const foundPath = await this.findFunctionInSubdirectories(
|
1267
|
+
this.controller!.getAppwriteFolderPath(),
|
1268
|
+
func.name
|
1269
|
+
);
|
1270
|
+
|
1271
|
+
if (foundPath) {
|
1272
|
+
functionPath = foundPath;
|
1273
|
+
}
|
1274
|
+
}
|
1275
|
+
|
1276
|
+
const { action } = await inquirer.prompt([
|
1132
1277
|
{
|
1133
|
-
type: "
|
1134
|
-
name: "
|
1135
|
-
message: `Function "${func.name}"
|
1136
|
-
|
1278
|
+
type: "list",
|
1279
|
+
name: "action",
|
1280
|
+
message: `Function "${func.name}" ${
|
1281
|
+
functionPath ? "found at " + functionPath : "not found locally"
|
1282
|
+
}. What would you like to do?`,
|
1283
|
+
choices: [
|
1284
|
+
...(functionPath
|
1285
|
+
? [
|
1286
|
+
{
|
1287
|
+
name: "Deploy to remote",
|
1288
|
+
value: "deploy",
|
1289
|
+
},
|
1290
|
+
]
|
1291
|
+
: []),
|
1292
|
+
{ name: "Skip this function", value: "skip" },
|
1293
|
+
],
|
1137
1294
|
},
|
1138
1295
|
]);
|
1139
1296
|
|
1140
|
-
if (deploy) {
|
1297
|
+
if (action === "deploy" && functionPath) {
|
1141
1298
|
await this.controller!.deployFunction(func.name);
|
1142
1299
|
}
|
1143
1300
|
} else if (hasRemote) {
|
1144
|
-
const {
|
1301
|
+
const { action } = await inquirer.prompt([
|
1145
1302
|
{
|
1146
|
-
type: "
|
1147
|
-
name: "
|
1148
|
-
message: `Function "${func.name}" exists only remotely.
|
1149
|
-
|
1303
|
+
type: "list",
|
1304
|
+
name: "action",
|
1305
|
+
message: `Function "${func.name}" exists only remotely. What would you like to do?`,
|
1306
|
+
choices: [
|
1307
|
+
{ name: "Update config only", value: "config" },
|
1308
|
+
{ name: "Download locally", value: "download" },
|
1309
|
+
{ name: "Skip this function", value: "skip" },
|
1310
|
+
],
|
1150
1311
|
},
|
1151
1312
|
]);
|
1152
1313
|
|
1153
|
-
if (download) {
|
1314
|
+
if (action === "download") {
|
1154
1315
|
await downloadLatestFunctionDeployment(
|
1155
1316
|
this.controller!.appwriteServer!,
|
1156
1317
|
func.$id,
|
1157
1318
|
join(this.controller!.getAppwriteFolderPath(), "functions")
|
1158
1319
|
);
|
1320
|
+
} else if (action === "config") {
|
1321
|
+
const remoteFunction = await getFunction(
|
1322
|
+
this.controller!.appwriteServer!,
|
1323
|
+
func.$id
|
1324
|
+
);
|
1325
|
+
|
1326
|
+
const newFunction = {
|
1327
|
+
$id: remoteFunction.$id,
|
1328
|
+
name: remoteFunction.name,
|
1329
|
+
runtime: remoteFunction.runtime as Runtime,
|
1330
|
+
execute: remoteFunction.execute || [],
|
1331
|
+
events: remoteFunction.events || [],
|
1332
|
+
schedule: remoteFunction.schedule || "",
|
1333
|
+
timeout: remoteFunction.timeout || 15,
|
1334
|
+
enabled: remoteFunction.enabled !== false,
|
1335
|
+
logging: remoteFunction.logging !== false,
|
1336
|
+
entrypoint: remoteFunction.entrypoint || "src/index.ts",
|
1337
|
+
commands: remoteFunction.commands || "npm install",
|
1338
|
+
scopes: (remoteFunction.scopes || []) as FunctionScope[],
|
1339
|
+
installationId: remoteFunction.installationId,
|
1340
|
+
providerRepositoryId: remoteFunction.providerRepositoryId,
|
1341
|
+
providerBranch: remoteFunction.providerBranch,
|
1342
|
+
providerSilentMode: remoteFunction.providerSilentMode,
|
1343
|
+
providerRootDirectory: remoteFunction.providerRootDirectory,
|
1344
|
+
specification: remoteFunction.specification as Specification,
|
1345
|
+
};
|
1346
|
+
|
1347
|
+
this.controller!.config!.functions =
|
1348
|
+
this.controller!.config!.functions || [];
|
1349
|
+
this.controller!.config!.functions.push(newFunction);
|
1350
|
+
console.log(
|
1351
|
+
chalk.green(`Added config for remote function: ${func.name}`)
|
1352
|
+
);
|
1159
1353
|
}
|
1160
1354
|
}
|
1161
1355
|
}
|
1356
|
+
|
1357
|
+
// Update schemas after all changes
|
1358
|
+
const schemaGenerator = new SchemaGenerator(
|
1359
|
+
this.controller!.config!,
|
1360
|
+
this.controller!.getAppwriteFolderPath()
|
1361
|
+
);
|
1362
|
+
schemaGenerator.updateConfig(this.controller!.config!);
|
1162
1363
|
}
|
1163
1364
|
|
1164
1365
|
console.log(chalk.green("✨ Configurations synchronized successfully!"));
|