appwrite-utils-cli 0.9.991 → 0.9.992
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 +25 -0
- package/dist/functions/methods.d.ts +10 -0
- package/dist/functions/methods.js +79 -0
- package/dist/interactiveCLI.d.ts +1 -0
- package/dist/interactiveCLI.js +46 -2
- package/dist/main.js +36 -0
- package/dist/utilsController.d.ts +2 -1
- package/dist/utilsController.js +11 -1
- package/package.json +3 -2
- package/src/functions/methods.ts +235 -0
- package/src/interactiveCLI.ts +49 -2
- package/src/main.ts +41 -0
- package/src/utilsController.ts +12 -2
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,7 @@ This updated CLI ensures that developers have robust tools at their fingertips t
|
|
125
149
|
|
126
150
|
## Changelog
|
127
151
|
|
152
|
+
- 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
153
|
- 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
154
|
- 0.9.983: Fixed `afterImportActions` not resolving
|
130
155
|
- 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, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, 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
|
+
};
|
package/dist/interactiveCLI.d.ts
CHANGED
package/dist/interactiveCLI.js
CHANGED
@@ -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:
|
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:
|
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
|
}
|
package/dist/utilsController.js
CHANGED
@@ -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.
|
4
|
+
"version": "0.9.992",
|
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.
|
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
|
+
undefined,
|
109
|
+
undefined,
|
110
|
+
undefined,
|
111
|
+
undefined,
|
112
|
+
undefined,
|
113
|
+
undefined,
|
114
|
+
undefined,
|
115
|
+
undefined,
|
116
|
+
undefined,
|
117
|
+
undefined,
|
118
|
+
undefined,
|
119
|
+
undefined,
|
120
|
+
undefined,
|
121
|
+
undefined,
|
122
|
+
undefined,
|
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
|
+
};
|
package/src/interactiveCLI.ts
CHANGED
@@ -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:
|
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:
|
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!);
|
package/src/utilsController.ts
CHANGED
@@ -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
|
}
|