appwrite-utils-cli 0.9.991 → 0.9.993

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
@@ -82,6 +82,11 @@ Available options:
82
82
  - `--remoteProjectId`: Set the remote Appwrite project ID for transfers
83
83
  - `--remoteApiKey`: Set the remote Appwrite API key for transfers
84
84
  - `--setup`: Create setup files
85
+ - `--updateFunctionSpec`: Update function specifications
86
+ - `--functionId`: Function ID to update
87
+ - `--specification`: New function specification (one of: s-0.5vcpu-512mb, s-1vcpu-1gb, s-2vcpu-2gb, s-2vcpu-4gb, s-4vcpu-4gb, s-4vcpu-8gb, s-8vcpu-4gb, s-8vcpu-8gb)
88
+
89
+
85
90
 
86
91
  ## Examples
87
92
 
@@ -109,6 +114,25 @@ Transfer files between buckets:
109
114
  npx appwrite-utils-cli appwrite-migrate --transfer --fromBucketId sourceBucketId --toBucketId targetBucketId --remoteEndpoint https://appwrite.otherserver.com --remoteProjectId yourProjectId --remoteApiKey yourApiKey
110
115
  ```
111
116
 
117
+ ### Update Function Specifications
118
+
119
+ Update the CPU and RAM specifications for a function:
120
+
121
+ ```bash
122
+ npx appwrite-utils-cli appwrite-migrate --updateFunctionSpec --functionId yourFunctionId --specification s-1vcpu-1gb
123
+ ```
124
+
125
+ Available specifications:
126
+
127
+ - s-0.5vcpu-512mb: 0.5 vCPU, 512MB RAM
128
+ - s-1vcpu-1gb: 1 vCPU, 1GB RAM
129
+ - s-2vcpu-2gb: 2 vCPU, 2GB RAM
130
+ - s-2vcpu-4gb: 2 vCPU, 4GB RAM
131
+ - s-4vcpu-4gb: 4 vCPU, 4GB RAM
132
+ - s-4vcpu-8gb: 4 vCPU, 8GB RAM
133
+ - s-8vcpu-4gb: 8 vCPU, 4GB RAM
134
+ - s-8vcpu-8gb: 8 vCPU, 8GB RAM
135
+
112
136
  ## Additional Notes
113
137
 
114
138
  - If you run out of RAM during large data imports, you can increase Node's memory allocation:
@@ -125,6 +149,8 @@ This updated CLI ensures that developers have robust tools at their fingertips t
125
149
 
126
150
  ## Changelog
127
151
 
152
+ - 0.9.993: Fixed `updateFunctionSpecifications` resetting functions to default with undefined values (oops)
153
+ - 0.9.992: Added `updateFunctionSpecifications` which lists functions and specifications to allow you to update your functions max CPU and RAM usage per-function
128
154
  - 0.9.990: Fixed `transferFilesLocalToLocal` and `remote` if a document exists with that `$id`, also fixed wipe `"all"` option also wiping the associated buckets
129
155
  - 0.9.983: Fixed `afterImportActions` not resolving
130
156
  - 0.9.981: Try fixing `tryAwaitWithRetry` to catch `522` errors from Cloudflare, they were appearing for some users, also added a 1000ms delay to `tryAwaitWithRetry`
@@ -0,0 +1,10 @@
1
+ import { Client, Runtime, type Models } from "node-appwrite";
2
+ import { type Specification } from "appwrite-utils";
3
+ export declare const listFunctions: (client: Client, queries?: string[], search?: string) => Promise<Models.FunctionList>;
4
+ export declare const getFunction: (client: Client, functionId: string) => Promise<Models.Function>;
5
+ export declare const deleteFunction: (client: Client, functionId: string) => Promise<{}>;
6
+ export declare const createFunction: (client: Client, functionId: string, name: string, runtime: Runtime, execute?: string[], events?: string[], schedule?: string, timeout?: number, enabled?: boolean, logging?: boolean, entrypoint?: string, commands?: string, scopes?: string[], installationId?: string, providerRepositoryId?: string, providerBranch?: string, providerSilentMode?: boolean, providerRootDirectory?: string, templateRepository?: string, templateOwner?: string, templateRootDirectory?: string, templateVersion?: string, specification?: string) => Promise<Models.Function>;
7
+ export declare const updateFunctionSpecifications: (client: Client, functionId: string, specification: Specification) => Promise<Models.Function | undefined>;
8
+ export declare const listSpecifications: (client: Client) => Promise<Models.SpecificationList>;
9
+ export declare const updateFunction: (client: Client, functionId: string, name: string, runtime?: Runtime, execute?: string[], events?: string[], schedule?: string, timeout?: number, enabled?: boolean, logging?: boolean, entrypoint?: string, commands?: string, scopes?: string[], installationId?: string, providerRepositoryId?: string, providerBranch?: string, providerSilentMode?: boolean, providerRootDirectory?: string, specification?: Specification) => Promise<Models.Function>;
10
+ export declare const deployFunction: (client: Client, functionId: string, codePath: string, activate?: boolean, entrypoint?: string, commands?: string) => Promise<Models.Deployment>;
@@ -0,0 +1,79 @@
1
+ import { AppwriteException, Client, Functions, Query, Runtime, } from "node-appwrite";
2
+ import { InputFile } from "node-appwrite/file";
3
+ import { create as createTarball } from "tar";
4
+ import { join } from "node:path";
5
+ import fs from "node:fs";
6
+ import {} from "appwrite-utils";
7
+ import chalk from "chalk";
8
+ export const listFunctions = async (client, queries, search) => {
9
+ const functions = new Functions(client);
10
+ const functionsList = await functions.list(queries, search);
11
+ return functionsList;
12
+ };
13
+ export const getFunction = async (client, functionId) => {
14
+ const functions = new Functions(client);
15
+ const functionResponse = await functions.get(functionId);
16
+ return functionResponse;
17
+ };
18
+ export const deleteFunction = async (client, functionId) => {
19
+ const functions = new Functions(client);
20
+ const functionResponse = await functions.delete(functionId);
21
+ return functionResponse;
22
+ };
23
+ export const createFunction = async (client, functionId, name, runtime, execute, events, schedule, timeout, enabled, logging, entrypoint, commands, scopes, installationId, providerRepositoryId, providerBranch, providerSilentMode, providerRootDirectory, templateRepository, templateOwner, templateRootDirectory, templateVersion, specification) => {
24
+ const functions = new Functions(client);
25
+ const functionResponse = await functions.create(functionId, name, runtime, execute, events, schedule, timeout, enabled, logging, entrypoint, commands, scopes, installationId, providerRepositoryId, providerBranch, providerSilentMode, providerRootDirectory, templateRepository, templateOwner, templateRootDirectory, templateVersion, specification);
26
+ return functionResponse;
27
+ };
28
+ export const updateFunctionSpecifications = async (client, functionId, specification) => {
29
+ const curFunction = await listFunctions(client, [
30
+ Query.equal("$id", functionId),
31
+ ]);
32
+ if (curFunction.functions.length === 0) {
33
+ throw new Error("Function not found");
34
+ }
35
+ const functionFound = curFunction.functions[0];
36
+ try {
37
+ const functionResponse = await updateFunction(client, functionId, functionFound.name, functionFound.runtime, functionFound.execute, functionFound.events, functionFound.schedule, functionFound.timeout, functionFound.enabled, functionFound.logging, functionFound.entrypoint, functionFound.commands, functionFound.scopes, functionFound.installationId, functionFound.providerRepositoryId, functionFound.providerBranch, functionFound.providerSilentMode, functionFound.providerRootDirectory, specification);
38
+ return functionResponse;
39
+ }
40
+ catch (error) {
41
+ if (error instanceof AppwriteException &&
42
+ error.message.includes("Invalid `specification`")) {
43
+ console.error(chalk.red("Error updating function specifications, please try setting the env variable `_FUNCTIONS_CPUS` and `_FUNCTIONS_RAM` to non-zero values"));
44
+ }
45
+ else {
46
+ console.error(chalk.red("Error updating function specifications."));
47
+ throw error;
48
+ }
49
+ }
50
+ };
51
+ export const listSpecifications = async (client) => {
52
+ const functions = new Functions(client);
53
+ const specifications = await functions.listSpecifications();
54
+ return specifications;
55
+ };
56
+ export const updateFunction = async (client, functionId, name, runtime, execute, events, schedule, timeout, enabled, logging, entrypoint, commands, scopes, installationId, providerRepositoryId, providerBranch, providerSilentMode, providerRootDirectory, specification) => {
57
+ const functions = new Functions(client);
58
+ const functionResponse = await functions.update(functionId, name, runtime, execute, events, schedule, timeout, enabled, logging, entrypoint, commands, scopes, installationId, providerRepositoryId, providerBranch, providerSilentMode, providerRootDirectory, specification);
59
+ return functionResponse;
60
+ };
61
+ export const deployFunction = async (client, functionId, codePath, activate = true, entrypoint = "index.js", commands = "npm install") => {
62
+ const functions = new Functions(client);
63
+ // Create temporary tar.gz file
64
+ const tarPath = join(process.cwd(), `function-${functionId}.tar.gz`);
65
+ await createTarball({
66
+ gzip: true,
67
+ file: tarPath,
68
+ cwd: codePath,
69
+ }, ["."] // Include all files from codePath
70
+ );
71
+ // Read the tar.gz file and create a File object
72
+ const fileBuffer = await fs.promises.readFile(tarPath);
73
+ const fileObject = InputFile.fromBuffer(new Uint8Array(fileBuffer), `function-${functionId}.tar.gz`);
74
+ // Create deployment with the File object
75
+ const functionResponse = await functions.createDeployment(functionId, fileObject, activate, entrypoint, commands);
76
+ // Clean up the temporary tar file
77
+ await fs.promises.unlink(tarPath);
78
+ return functionResponse;
79
+ };
@@ -21,4 +21,5 @@ export declare class InteractiveCLI {
21
21
  private getLocalCollections;
22
22
  private getLocalDatabases;
23
23
  private reloadConfig;
24
+ private updateFunctionSpec;
24
25
  }
@@ -10,6 +10,7 @@ import { parseAttribute, PermissionToAppwritePermission, } from "appwrite-utils"
10
10
  import { ulid } from "ulidx";
11
11
  import chalk from "chalk";
12
12
  import { DateTime } from "luxon";
13
+ import { listFunctions, listSpecifications } from "./functions/methods.js";
13
14
  var CHOICES;
14
15
  (function (CHOICES) {
15
16
  CHOICES["CREATE_COLLECTION_CONFIG"] = "Create collection config file";
@@ -24,6 +25,7 @@ var CHOICES;
24
25
  CHOICES["GENERATE_SCHEMAS"] = "Generate schemas";
25
26
  CHOICES["IMPORT_DATA"] = "Import data";
26
27
  CHOICES["RELOAD_CONFIG"] = "Reload configuration files";
28
+ CHOICES["UPDATE_FUNCTION_SPEC"] = "Update function specifications";
27
29
  CHOICES["EXIT"] = "Exit";
28
30
  })(CHOICES || (CHOICES = {}));
29
31
  export class InteractiveCLI {
@@ -91,6 +93,10 @@ export class InteractiveCLI {
91
93
  await this.initControllerIfNeeded();
92
94
  await this.reloadConfig();
93
95
  break;
96
+ case CHOICES.UPDATE_FUNCTION_SPEC:
97
+ await this.initControllerIfNeeded();
98
+ await this.updateFunctionSpec();
99
+ break;
94
100
  case CHOICES.EXIT:
95
101
  console.log(chalk.green("Goodbye!"));
96
102
  return;
@@ -141,7 +147,7 @@ export class InteractiveCLI {
141
147
  name: "selectedDatabases",
142
148
  message: chalk.blue(message),
143
149
  choices,
144
- loop: false,
150
+ loop: true,
145
151
  pageSize: 10,
146
152
  },
147
153
  ]);
@@ -190,7 +196,7 @@ export class InteractiveCLI {
190
196
  name: "selectedCollections",
191
197
  message: chalk.blue(message),
192
198
  choices,
193
- loop: false,
199
+ loop: true,
194
200
  pageSize: 10,
195
201
  },
196
202
  ]);
@@ -689,4 +695,42 @@ export class InteractiveCLI {
689
695
  console.error(chalk.red("Error reloading configuration files:"), error);
690
696
  }
691
697
  }
698
+ async updateFunctionSpec() {
699
+ const functions = await listFunctions(this.controller.appwriteServer, [
700
+ Query.limit(1000),
701
+ ]);
702
+ const functionsToUpdate = await inquirer.prompt([
703
+ {
704
+ type: 'checkbox',
705
+ name: 'functionId',
706
+ message: 'Select functions to update:',
707
+ choices: functions.functions.map(f => ({
708
+ name: `${f.name} (${f.$id})`,
709
+ value: f.$id
710
+ })),
711
+ loop: true,
712
+ }
713
+ ]);
714
+ const specifications = await listSpecifications(this.controller.appwriteServer);
715
+ const { specification } = await inquirer.prompt([
716
+ {
717
+ type: 'list',
718
+ name: 'specification',
719
+ message: 'Select new specification:',
720
+ choices: specifications.specifications.map((s) => ({
721
+ name: `${s.slug}`,
722
+ value: s.slug
723
+ })),
724
+ }
725
+ ]);
726
+ try {
727
+ for (const functionId of functionsToUpdate.functionId) {
728
+ await this.controller.updateFunctionSpecifications(functionId, specification);
729
+ console.log(chalk.green(`Successfully updated function specification to ${specification}`));
730
+ }
731
+ }
732
+ catch (error) {
733
+ console.error(chalk.red('Error updating function specification:'), error);
734
+ }
735
+ }
692
736
  }
package/dist/main.js CHANGED
@@ -9,6 +9,8 @@ import { getClient } from "./utils/getClientFromConfig.js";
9
9
  import { fetchAllDatabases } from "./migrations/databases.js";
10
10
  import { setupDirsFiles } from "./utils/setupFiles.js";
11
11
  import { fetchAllCollections } from "./collections/methods.js";
12
+ import chalk from "chalk";
13
+ import { listSpecifications } from "./functions/methods.js";
12
14
  const argv = yargs(hideBin(process.argv))
13
15
  .option("it", {
14
16
  alias: ["interactive", "i"],
@@ -119,6 +121,28 @@ const argv = yargs(hideBin(process.argv))
119
121
  .option("setup", {
120
122
  type: "boolean",
121
123
  description: "Setup directories and files",
124
+ })
125
+ .option("updateFunctionSpec", {
126
+ type: "boolean",
127
+ description: "Update function specifications",
128
+ })
129
+ .option("functionId", {
130
+ type: "string",
131
+ description: "Function ID to update",
132
+ })
133
+ .option("specification", {
134
+ type: "string",
135
+ description: "New function specification (e.g., 's-1vcpu-1gb')",
136
+ choices: [
137
+ "s-0.5vcpu-512mb",
138
+ "s-1vcpu-1gb",
139
+ "s-2vcpu-2gb",
140
+ "s-2vcpu-4gb",
141
+ "s-4vcpu-4gb",
142
+ "s-4vcpu-8gb",
143
+ "s-8vcpu-4gb",
144
+ "s-8vcpu-8gb"
145
+ ],
122
146
  })
123
147
  .parse();
124
148
  async function main() {
@@ -148,6 +172,18 @@ async function main() {
148
172
  shouldWriteFile: parsedArgv.writeData,
149
173
  wipeCollections: parsedArgv.wipeCollections,
150
174
  };
175
+ if (parsedArgv.updateFunctionSpec) {
176
+ if (!parsedArgv.functionId || !parsedArgv.specification) {
177
+ throw new Error("Function ID and specification are required for updating function specs");
178
+ }
179
+ console.log(chalk.yellow(`Updating function specification for ${parsedArgv.functionId} to ${parsedArgv.specification}, checking if specification exists...`));
180
+ const specifications = await listSpecifications(controller.appwriteServer);
181
+ if (!specifications.specifications.some((s) => s.slug === parsedArgv.specification)) {
182
+ console.log(chalk.red(`Specification ${parsedArgv.specification} not found`));
183
+ return;
184
+ }
185
+ await controller.updateFunctionSpecifications(parsedArgv.functionId, parsedArgv.specification);
186
+ }
151
187
  // Add default databases if not specified
152
188
  if (!options.databases || options.databases.length === 0) {
153
189
  const allDatabases = await fetchAllDatabases(controller.database);
@@ -1,5 +1,5 @@
1
1
  import { Client, Databases, Storage, type Models } from "node-appwrite";
2
- import { type AppwriteConfig } from "appwrite-utils";
2
+ import { type AppwriteConfig, type Specification } from "appwrite-utils";
3
3
  import { type AfterImportActions, type ConverterFunctions, type ValidationRules } from "appwrite-utils";
4
4
  import { type TransferOptions } from "./migrations/transfer.js";
5
5
  export interface SetupOptions {
@@ -51,4 +51,5 @@ export declare class UtilsController {
51
51
  syncDb(databases?: Models.Database[], collections?: Models.Collection[]): Promise<void>;
52
52
  getAppwriteFolderPath(): string;
53
53
  transferData(options: TransferOptions): Promise<void>;
54
+ updateFunctionSpecifications(functionId: string, specification: Specification): Promise<void>;
54
55
  }
@@ -14,6 +14,8 @@ import { afterImportActions } from "./migrations/afterImportActions.js";
14
14
  import { transferDatabaseLocalToLocal, transferDatabaseLocalToRemote, transferStorageLocalToLocal, transferStorageLocalToRemote, } from "./migrations/transfer.js";
15
15
  import { getClient } from "./utils/getClientFromConfig.js";
16
16
  import { fetchAllDatabases } from "./migrations/databases.js";
17
+ import { updateFunctionSpecifications } from "./functions/methods.js";
18
+ import chalk from "chalk";
17
19
  export class UtilsController {
18
20
  appwriteFolderPath;
19
21
  appwriteConfigPath;
@@ -277,6 +279,14 @@ export class UtilsController {
277
279
  }
278
280
  }
279
281
  }
280
- console.log("Transfer completed");
282
+ console.log(chalk.green("Transfer completed"));
283
+ }
284
+ async updateFunctionSpecifications(functionId, specification) {
285
+ await this.init();
286
+ if (!this.appwriteServer)
287
+ throw new Error("Appwrite server not initialized");
288
+ console.log(chalk.green(`Updating function specifications for ${functionId} to ${specification}`));
289
+ await updateFunctionSpecifications(this.appwriteServer, functionId, specification);
290
+ console.log(chalk.green(`Successfully updated function specifications for ${functionId} to ${specification}`));
281
291
  }
282
292
  }
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.9.991",
4
+ "version": "0.9.993",
5
5
  "main": "src/main.ts",
6
6
  "type": "module",
7
7
  "repository": {
@@ -31,7 +31,7 @@
31
31
  },
32
32
  "dependencies": {
33
33
  "@types/inquirer": "^9.0.7",
34
- "appwrite-utils": "^0.3.95",
34
+ "appwrite-utils": "^0.3.96",
35
35
  "chalk": "^5.3.0",
36
36
  "commander": "^12.1.0",
37
37
  "inquirer": "^9.3.6",
@@ -40,6 +40,7 @@
40
40
  "luxon": "^3.5.0",
41
41
  "nanostores": "^0.10.3",
42
42
  "node-appwrite": "^14.1.0",
43
+ "tar": "^7.4.3",
43
44
  "tsx": "^4.17.0",
44
45
  "ulidx": "^2.4.0",
45
46
  "winston": "^3.14.2",
@@ -0,0 +1,235 @@
1
+ import {
2
+ AppwriteException,
3
+ Client,
4
+ Functions,
5
+ Query,
6
+ Runtime,
7
+ type Models,
8
+ } from "node-appwrite";
9
+ import { InputFile } from "node-appwrite/file";
10
+ import { create as createTarball } from "tar";
11
+ import { join } from "node:path";
12
+ import fs from "node:fs";
13
+ import { type Specification } from "appwrite-utils";
14
+ import chalk from "chalk";
15
+
16
+ export const listFunctions = async (
17
+ client: Client,
18
+ queries?: string[],
19
+ search?: string
20
+ ) => {
21
+ const functions = new Functions(client);
22
+ const functionsList = await functions.list(queries, search);
23
+ return functionsList;
24
+ };
25
+
26
+ export const getFunction = async (client: Client, functionId: string) => {
27
+ const functions = new Functions(client);
28
+ const functionResponse = await functions.get(functionId);
29
+ return functionResponse;
30
+ };
31
+
32
+ export const deleteFunction = async (client: Client, functionId: string) => {
33
+ const functions = new Functions(client);
34
+ const functionResponse = await functions.delete(functionId);
35
+ return functionResponse;
36
+ };
37
+
38
+ export const createFunction = async (
39
+ client: Client,
40
+ functionId: string,
41
+ name: string,
42
+ runtime: Runtime,
43
+ execute?: string[],
44
+ events?: string[],
45
+ schedule?: string,
46
+ timeout?: number,
47
+ enabled?: boolean,
48
+ logging?: boolean,
49
+ entrypoint?: string,
50
+ commands?: string,
51
+ scopes?: string[],
52
+ installationId?: string,
53
+ providerRepositoryId?: string,
54
+ providerBranch?: string,
55
+ providerSilentMode?: boolean,
56
+ providerRootDirectory?: string,
57
+ templateRepository?: string,
58
+ templateOwner?: string,
59
+ templateRootDirectory?: string,
60
+ templateVersion?: string,
61
+ specification?: string
62
+ ) => {
63
+ const functions = new Functions(client);
64
+ const functionResponse = await functions.create(
65
+ functionId,
66
+ name,
67
+ runtime,
68
+ execute,
69
+ events,
70
+ schedule,
71
+ timeout,
72
+ enabled,
73
+ logging,
74
+ entrypoint,
75
+ commands,
76
+ scopes,
77
+ installationId,
78
+ providerRepositoryId,
79
+ providerBranch,
80
+ providerSilentMode,
81
+ providerRootDirectory,
82
+ templateRepository,
83
+ templateOwner,
84
+ templateRootDirectory,
85
+ templateVersion,
86
+ specification
87
+ );
88
+ return functionResponse;
89
+ };
90
+
91
+ export const updateFunctionSpecifications = async (
92
+ client: Client,
93
+ functionId: string,
94
+ specification: Specification
95
+ ) => {
96
+ const curFunction = await listFunctions(client, [
97
+ Query.equal("$id", functionId),
98
+ ]);
99
+ if (curFunction.functions.length === 0) {
100
+ throw new Error("Function not found");
101
+ }
102
+ const functionFound = curFunction.functions[0];
103
+ try {
104
+ const functionResponse = await updateFunction(
105
+ client,
106
+ functionId,
107
+ functionFound.name,
108
+ functionFound.runtime as Runtime,
109
+ functionFound.execute,
110
+ functionFound.events,
111
+ functionFound.schedule,
112
+ functionFound.timeout,
113
+ functionFound.enabled,
114
+ functionFound.logging,
115
+ functionFound.entrypoint,
116
+ functionFound.commands,
117
+ functionFound.scopes,
118
+ functionFound.installationId,
119
+ functionFound.providerRepositoryId,
120
+ functionFound.providerBranch,
121
+ functionFound.providerSilentMode,
122
+ functionFound.providerRootDirectory,
123
+ specification
124
+ );
125
+ return functionResponse;
126
+ } catch (error) {
127
+ if (
128
+ error instanceof AppwriteException &&
129
+ error.message.includes("Invalid `specification`")
130
+ ) {
131
+ console.error(
132
+ chalk.red(
133
+ "Error updating function specifications, please try setting the env variable `_FUNCTIONS_CPUS` and `_FUNCTIONS_RAM` to non-zero values"
134
+ )
135
+ );
136
+ } else {
137
+ console.error(chalk.red("Error updating function specifications."));
138
+ throw error;
139
+ }
140
+ }
141
+ };
142
+
143
+ export const listSpecifications = async (client: Client) => {
144
+ const functions = new Functions(client);
145
+ const specifications = await functions.listSpecifications();
146
+ return specifications;
147
+ };
148
+
149
+ export const updateFunction = async (
150
+ client: Client,
151
+ functionId: string,
152
+ name: string,
153
+ runtime?: Runtime,
154
+ execute?: string[],
155
+ events?: string[],
156
+ schedule?: string,
157
+ timeout?: number,
158
+ enabled?: boolean,
159
+ logging?: boolean,
160
+ entrypoint?: string,
161
+ commands?: string,
162
+ scopes?: string[],
163
+ installationId?: string,
164
+ providerRepositoryId?: string,
165
+ providerBranch?: string,
166
+ providerSilentMode?: boolean,
167
+ providerRootDirectory?: string,
168
+ specification?: Specification
169
+ ) => {
170
+ const functions = new Functions(client);
171
+ const functionResponse = await functions.update(
172
+ functionId,
173
+ name,
174
+ runtime,
175
+ execute,
176
+ events,
177
+ schedule,
178
+ timeout,
179
+ enabled,
180
+ logging,
181
+ entrypoint,
182
+ commands,
183
+ scopes,
184
+ installationId,
185
+ providerRepositoryId,
186
+ providerBranch,
187
+ providerSilentMode,
188
+ providerRootDirectory,
189
+ specification
190
+ );
191
+ return functionResponse;
192
+ };
193
+
194
+ export const deployFunction = async (
195
+ client: Client,
196
+ functionId: string,
197
+ codePath: string,
198
+ activate: boolean = true,
199
+ entrypoint: string = "index.js",
200
+ commands: string = "npm install"
201
+ ) => {
202
+ const functions = new Functions(client);
203
+
204
+ // Create temporary tar.gz file
205
+ const tarPath = join(process.cwd(), `function-${functionId}.tar.gz`);
206
+ await createTarball(
207
+ {
208
+ gzip: true,
209
+ file: tarPath,
210
+ cwd: codePath,
211
+ },
212
+ ["."] // Include all files from codePath
213
+ );
214
+
215
+ // Read the tar.gz file and create a File object
216
+ const fileBuffer = await fs.promises.readFile(tarPath);
217
+ const fileObject = InputFile.fromBuffer(
218
+ new Uint8Array(fileBuffer),
219
+ `function-${functionId}.tar.gz`
220
+ );
221
+
222
+ // Create deployment with the File object
223
+ const functionResponse = await functions.createDeployment(
224
+ functionId,
225
+ fileObject,
226
+ activate,
227
+ entrypoint,
228
+ commands
229
+ );
230
+
231
+ // Clean up the temporary tar file
232
+ await fs.promises.unlink(tarPath);
233
+
234
+ return functionResponse;
235
+ };
@@ -23,6 +23,7 @@ import {
23
23
  import { ulid } from "ulidx";
24
24
  import chalk from "chalk";
25
25
  import { DateTime } from "luxon";
26
+ import { listFunctions, listSpecifications } from "./functions/methods.js";
26
27
 
27
28
  enum CHOICES {
28
29
  CREATE_COLLECTION_CONFIG = "Create collection config file",
@@ -37,6 +38,7 @@ enum CHOICES {
37
38
  GENERATE_SCHEMAS = "Generate schemas",
38
39
  IMPORT_DATA = "Import data",
39
40
  RELOAD_CONFIG = "Reload configuration files",
41
+ UPDATE_FUNCTION_SPEC = "Update function specifications",
40
42
  EXIT = "Exit",
41
43
  }
42
44
 
@@ -112,6 +114,10 @@ export class InteractiveCLI {
112
114
  await this.initControllerIfNeeded();
113
115
  await this.reloadConfig();
114
116
  break;
117
+ case CHOICES.UPDATE_FUNCTION_SPEC:
118
+ await this.initControllerIfNeeded();
119
+ await this.updateFunctionSpec();
120
+ break;
115
121
  case CHOICES.EXIT:
116
122
  console.log(chalk.green("Goodbye!"));
117
123
  return;
@@ -176,7 +182,7 @@ export class InteractiveCLI {
176
182
  name: "selectedDatabases",
177
183
  message: chalk.blue(message),
178
184
  choices,
179
- loop: false,
185
+ loop: true,
180
186
  pageSize: 10,
181
187
  },
182
188
  ]);
@@ -256,7 +262,7 @@ export class InteractiveCLI {
256
262
  name: "selectedCollections",
257
263
  message: chalk.blue(message),
258
264
  choices,
259
- loop: false,
265
+ loop: true,
260
266
  pageSize: 10,
261
267
  },
262
268
  ]);
@@ -962,4 +968,45 @@ export class InteractiveCLI {
962
968
  console.error(chalk.red("Error reloading configuration files:"), error);
963
969
  }
964
970
  }
971
+
972
+ private async updateFunctionSpec(): Promise<void> {
973
+ const functions = await listFunctions(this.controller!.appwriteServer!, [
974
+ Query.limit(1000),
975
+ ]);
976
+
977
+ const functionsToUpdate = await inquirer.prompt([
978
+ {
979
+ type: 'checkbox',
980
+ name: 'functionId',
981
+ message: 'Select functions to update:',
982
+ choices: functions.functions.map(f => ({
983
+ name: `${f.name} (${f.$id})`,
984
+ value: f.$id
985
+ })),
986
+ loop: true,
987
+ }
988
+ ]);
989
+
990
+ const specifications = await listSpecifications(this.controller!.appwriteServer!);
991
+ const { specification } = await inquirer.prompt([
992
+ {
993
+ type: 'list',
994
+ name: 'specification',
995
+ message: 'Select new specification:',
996
+ choices: specifications.specifications.map((s) => ({
997
+ name: `${s.slug}`,
998
+ value: s.slug
999
+ })),
1000
+ }
1001
+ ]);
1002
+
1003
+ try {
1004
+ for (const functionId of functionsToUpdate.functionId) {
1005
+ await this.controller!.updateFunctionSpecifications(functionId, specification);
1006
+ console.log(chalk.green(`Successfully updated function specification to ${specification}`));
1007
+ }
1008
+ } catch (error) {
1009
+ console.error(chalk.red('Error updating function specification:'), error);
1010
+ }
1011
+ }
965
1012
  }
package/src/main.ts CHANGED
@@ -10,6 +10,9 @@ import { getClient } from "./utils/getClientFromConfig.js";
10
10
  import { fetchAllDatabases } from "./migrations/databases.js";
11
11
  import { setupDirsFiles } from "./utils/setupFiles.js";
12
12
  import { fetchAllCollections } from "./collections/methods.js";
13
+ import type { Specification } from "appwrite-utils";
14
+ import chalk from "chalk";
15
+ import { listSpecifications } from "./functions/methods.js";
13
16
 
14
17
  interface CliOptions {
15
18
  it?: boolean;
@@ -38,6 +41,9 @@ interface CliOptions {
38
41
  remoteProjectId?: string;
39
42
  remoteApiKey?: string;
40
43
  setup?: boolean;
44
+ updateFunctionSpec?: boolean;
45
+ functionId?: string;
46
+ specification?: string;
41
47
  }
42
48
 
43
49
  type ParsedArgv = ArgumentsCamelCase<CliOptions>;
@@ -157,6 +163,28 @@ const argv = yargs(hideBin(process.argv))
157
163
  type: "boolean",
158
164
  description: "Setup directories and files",
159
165
  })
166
+ .option("updateFunctionSpec", {
167
+ type: "boolean",
168
+ description: "Update function specifications",
169
+ })
170
+ .option("functionId", {
171
+ type: "string",
172
+ description: "Function ID to update",
173
+ })
174
+ .option("specification", {
175
+ type: "string",
176
+ description: "New function specification (e.g., 's-1vcpu-1gb')",
177
+ choices: [
178
+ "s-0.5vcpu-512mb",
179
+ "s-1vcpu-1gb",
180
+ "s-2vcpu-2gb",
181
+ "s-2vcpu-4gb",
182
+ "s-4vcpu-4gb",
183
+ "s-4vcpu-8gb",
184
+ "s-8vcpu-4gb",
185
+ "s-8vcpu-8gb"
186
+ ],
187
+ })
160
188
  .parse() as ParsedArgv;
161
189
 
162
190
  async function main() {
@@ -190,6 +218,19 @@ async function main() {
190
218
  wipeCollections: parsedArgv.wipeCollections,
191
219
  };
192
220
 
221
+ if (parsedArgv.updateFunctionSpec) {
222
+ if (!parsedArgv.functionId || !parsedArgv.specification) {
223
+ throw new Error("Function ID and specification are required for updating function specs");
224
+ }
225
+ console.log(chalk.yellow(`Updating function specification for ${parsedArgv.functionId} to ${parsedArgv.specification}, checking if specification exists...`));
226
+ const specifications = await listSpecifications(controller.appwriteServer!);
227
+ if (!specifications.specifications.some((s: { slug: string }) => s.slug === parsedArgv.specification)) {
228
+ console.log(chalk.red(`Specification ${parsedArgv.specification} not found`));
229
+ return;
230
+ }
231
+ await controller.updateFunctionSpecifications(parsedArgv.functionId, parsedArgv.specification as Specification);
232
+ }
233
+
193
234
  // Add default databases if not specified
194
235
  if (!options.databases || options.databases.length === 0) {
195
236
  const allDatabases = await fetchAllDatabases(controller.database!);
@@ -1,5 +1,5 @@
1
1
  import { Client, Databases, Query, Storage, type Models } from "node-appwrite";
2
- import { type AppwriteConfig } from "appwrite-utils";
2
+ import { type AppwriteConfig, type Specification } from "appwrite-utils";
3
3
  import { loadConfig, findAppwriteConfig } from "./utils/loadConfigs.js";
4
4
  import { UsersController } from "./migrations/users.js";
5
5
  import { AppwriteToX } from "./migrations/appwriteToX.js";
@@ -42,6 +42,8 @@ import {
42
42
  } from "./migrations/transfer.js";
43
43
  import { getClient } from "./utils/getClientFromConfig.js";
44
44
  import { fetchAllDatabases } from "./migrations/databases.js";
45
+ import { updateFunctionSpecifications } from "./functions/methods.js";
46
+ import chalk from "chalk";
45
47
 
46
48
  export interface SetupOptions {
47
49
  databases?: Models.Database[];
@@ -404,6 +406,14 @@ export class UtilsController {
404
406
  }
405
407
  }
406
408
 
407
- console.log("Transfer completed");
409
+ console.log(chalk.green("Transfer completed"));
410
+ }
411
+
412
+ async updateFunctionSpecifications(functionId: string, specification: Specification) {
413
+ await this.init();
414
+ if (!this.appwriteServer) throw new Error("Appwrite server not initialized");
415
+ console.log(chalk.green(`Updating function specifications for ${functionId} to ${specification}`));
416
+ await updateFunctionSpecifications(this.appwriteServer, functionId, specification);
417
+ console.log(chalk.green(`Successfully updated function specifications for ${functionId} to ${specification}`));
408
418
  }
409
419
  }