appwrite-utils-cli 0.9.993 → 0.9.995
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 -2
- package/dist/collections/methods.js +6 -4
- package/dist/functions/deployments.d.ts +4 -0
- package/dist/functions/deployments.js +114 -0
- package/dist/functions/methods.d.ts +13 -8
- package/dist/functions/methods.js +74 -20
- package/dist/functions/templates/count-docs-in-collection/src/main.d.ts +21 -0
- package/dist/functions/templates/count-docs-in-collection/src/main.js +114 -0
- package/dist/functions/templates/count-docs-in-collection/src/request.d.ts +15 -0
- package/dist/functions/templates/count-docs-in-collection/src/request.js +6 -0
- package/dist/functions/templates/typescript-node/src/index.d.ts +11 -0
- package/dist/functions/templates/typescript-node/src/index.js +11 -0
- package/dist/interactiveCLI.d.ts +6 -0
- package/dist/interactiveCLI.js +437 -32
- package/dist/main.js +0 -0
- package/dist/migrations/appwriteToX.js +32 -1
- package/dist/migrations/schemaStrings.d.ts +1 -0
- package/dist/migrations/schemaStrings.js +74 -2
- package/dist/migrations/transfer.js +112 -123
- package/dist/utils/loadConfigs.d.ts +1 -0
- package/dist/utils/loadConfigs.js +19 -0
- package/dist/utils/schemaStrings.js +27 -0
- package/dist/utilsController.d.ts +5 -1
- package/dist/utilsController.js +54 -2
- package/package.json +57 -55
- package/src/collections/methods.ts +29 -10
- package/src/functions/deployments.ts +190 -0
- package/src/functions/methods.ts +295 -235
- package/src/functions/templates/count-docs-in-collection/README.md +54 -0
- package/src/functions/templates/count-docs-in-collection/src/main.ts +159 -0
- package/src/functions/templates/count-docs-in-collection/src/request.ts +9 -0
- package/src/functions/templates/poetry/README.md +30 -0
- package/src/functions/templates/poetry/pyproject.toml +16 -0
- package/src/functions/templates/poetry/src/__init__.py +0 -0
- package/src/functions/templates/poetry/src/index.py +16 -0
- package/src/functions/templates/typescript-node/README.md +32 -0
- package/src/functions/templates/typescript-node/src/index.ts +23 -0
- package/src/interactiveCLI.ts +606 -47
- package/src/migrations/appwriteToX.ts +44 -1
- package/src/migrations/schemaStrings.ts +83 -2
- package/src/migrations/transfer.ts +280 -207
- package/src/setupController.ts +41 -41
- package/src/utils/loadConfigs.ts +24 -0
- package/src/utils/schemaStrings.ts +27 -0
- package/src/utilsController.ts +63 -3
- package/tsconfig.json +8 -1
package/package.json
CHANGED
@@ -1,55 +1,57 @@
|
|
1
|
-
{
|
2
|
-
"name": "appwrite-utils-cli",
|
3
|
-
"description": "Appwrite Utility Functions to help with database management, data conversion, data import, migrations, and much more. Meant to be used as a CLI tool, I do not recommend installing this in frontend environments.",
|
4
|
-
"version": "0.9.
|
5
|
-
"main": "src/main.ts",
|
6
|
-
"type": "module",
|
7
|
-
"repository": {
|
8
|
-
"type": "git",
|
9
|
-
"url": "https://github.com/zachhandley/AppwriteUtils"
|
10
|
-
},
|
11
|
-
"author": "Zach Handley <zach@blackleafdigital.com> (https://zachhandley.com)",
|
12
|
-
"keywords": [
|
13
|
-
"appwrite",
|
14
|
-
"cli",
|
15
|
-
"utils",
|
16
|
-
"migrations",
|
17
|
-
"data",
|
18
|
-
"database",
|
19
|
-
"import",
|
20
|
-
"migration",
|
21
|
-
"utility"
|
22
|
-
],
|
23
|
-
"bin": {
|
24
|
-
"appwrite-migrate": "./dist/main.js"
|
25
|
-
},
|
26
|
-
"scripts": {
|
27
|
-
"build": "bun run tsc",
|
28
|
-
"start": "tsx --no-cache src/main.ts",
|
29
|
-
"deploy": "bun run build && npm publish --access public",
|
30
|
-
"postinstall": "echo 'This package is intended for CLI use only and should not be added as a dependency in other projects.'"
|
31
|
-
},
|
32
|
-
"dependencies": {
|
33
|
-
"@types/inquirer": "^9.0.7",
|
34
|
-
"appwrite-utils": "^0.3.
|
35
|
-
"chalk": "^5.3.0",
|
36
|
-
"
|
37
|
-
"
|
38
|
-
"
|
39
|
-
"
|
40
|
-
"
|
41
|
-
"
|
42
|
-
"
|
43
|
-
"
|
44
|
-
"
|
45
|
-
"
|
46
|
-
"
|
47
|
-
"
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
"@types/
|
52
|
-
"@types/
|
53
|
-
"
|
54
|
-
|
55
|
-
|
1
|
+
{
|
2
|
+
"name": "appwrite-utils-cli",
|
3
|
+
"description": "Appwrite Utility Functions to help with database management, data conversion, data import, migrations, and much more. Meant to be used as a CLI tool, I do not recommend installing this in frontend environments.",
|
4
|
+
"version": "0.9.995",
|
5
|
+
"main": "src/main.ts",
|
6
|
+
"type": "module",
|
7
|
+
"repository": {
|
8
|
+
"type": "git",
|
9
|
+
"url": "https://github.com/zachhandley/AppwriteUtils"
|
10
|
+
},
|
11
|
+
"author": "Zach Handley <zach@blackleafdigital.com> (https://zachhandley.com)",
|
12
|
+
"keywords": [
|
13
|
+
"appwrite",
|
14
|
+
"cli",
|
15
|
+
"utils",
|
16
|
+
"migrations",
|
17
|
+
"data",
|
18
|
+
"database",
|
19
|
+
"import",
|
20
|
+
"migration",
|
21
|
+
"utility"
|
22
|
+
],
|
23
|
+
"bin": {
|
24
|
+
"appwrite-migrate": "./dist/main.js"
|
25
|
+
},
|
26
|
+
"scripts": {
|
27
|
+
"build": "bun run tsc",
|
28
|
+
"start": "tsx --no-cache src/main.ts",
|
29
|
+
"deploy": "bun run build && npm publish --access public",
|
30
|
+
"postinstall": "echo 'This package is intended for CLI use only and should not be added as a dependency in other projects.'"
|
31
|
+
},
|
32
|
+
"dependencies": {
|
33
|
+
"@types/inquirer": "^9.0.7",
|
34
|
+
"appwrite-utils": "^0.3.97",
|
35
|
+
"chalk": "^5.3.0",
|
36
|
+
"cli-progress": "^3.12.0",
|
37
|
+
"commander": "^12.1.0",
|
38
|
+
"inquirer": "^9.3.6",
|
39
|
+
"js-yaml": "^4.1.0",
|
40
|
+
"lodash": "^4.17.21",
|
41
|
+
"luxon": "^3.5.0",
|
42
|
+
"nanostores": "^0.10.3",
|
43
|
+
"node-appwrite": "^14.1.0",
|
44
|
+
"tar": "^7.4.3",
|
45
|
+
"tsx": "^4.17.0",
|
46
|
+
"ulidx": "^2.4.0",
|
47
|
+
"winston": "^3.14.2",
|
48
|
+
"zod": "^3.23.8"
|
49
|
+
},
|
50
|
+
"devDependencies": {
|
51
|
+
"@types/cli-progress": "^3.11.6",
|
52
|
+
"@types/js-yaml": "^4.0.9",
|
53
|
+
"@types/lodash": "^4.17.7",
|
54
|
+
"@types/luxon": "^3.4.2",
|
55
|
+
"typescript": "^5.5.4"
|
56
|
+
}
|
57
|
+
}
|
@@ -11,7 +11,7 @@ import { nameToIdMapping, processQueue } from "../migrations/queue.js";
|
|
11
11
|
import { createUpdateCollectionAttributes } from "./attributes.js";
|
12
12
|
import { createOrUpdateIndexes } from "./indexes.js";
|
13
13
|
import _ from "lodash";
|
14
|
-
import { SchemaGenerator } from "../
|
14
|
+
import { SchemaGenerator } from "../migrations/schemaStrings.js";
|
15
15
|
import { delay, tryAwaitWithRetry } from "../utils/helperFunctions.js";
|
16
16
|
|
17
17
|
export const documentExists = async (
|
@@ -124,20 +124,36 @@ export const fetchAndCacheCollectionByName = async (
|
|
124
124
|
}
|
125
125
|
};
|
126
126
|
|
127
|
-
async function wipeDocumentsFromCollection(
|
128
|
-
|
127
|
+
async function wipeDocumentsFromCollection(
|
128
|
+
database: Databases,
|
129
|
+
databaseId: string,
|
130
|
+
collectionId: string
|
131
|
+
) {
|
132
|
+
const initialDocuments = await database.listDocuments(
|
133
|
+
databaseId,
|
134
|
+
collectionId,
|
135
|
+
[Query.limit(1000)]
|
136
|
+
);
|
129
137
|
let documents = initialDocuments.documents;
|
130
138
|
while (documents.length === 1000) {
|
131
|
-
const docsResponse = await database.listDocuments(
|
139
|
+
const docsResponse = await database.listDocuments(
|
140
|
+
databaseId,
|
141
|
+
collectionId,
|
142
|
+
[Query.limit(1000)]
|
143
|
+
);
|
132
144
|
documents = documents.concat(docsResponse.documents);
|
133
145
|
}
|
134
|
-
const batchDeletePromises = documents.map(doc =>
|
146
|
+
const batchDeletePromises = documents.map((doc) =>
|
147
|
+
database.deleteDocument(databaseId, collectionId, doc.$id)
|
148
|
+
);
|
135
149
|
const maxStackSize = 100;
|
136
150
|
for (let i = 0; i < batchDeletePromises.length; i += maxStackSize) {
|
137
151
|
await Promise.all(batchDeletePromises.slice(i, i + maxStackSize));
|
138
152
|
await delay(100);
|
139
153
|
}
|
140
|
-
console.log(
|
154
|
+
console.log(
|
155
|
+
`Deleted ${documents.length} documents from collection ${collectionId}`
|
156
|
+
);
|
141
157
|
}
|
142
158
|
|
143
159
|
export const wipeDatabase = async (
|
@@ -167,7 +183,9 @@ export const wipeCollection = async (
|
|
167
183
|
databaseId: string,
|
168
184
|
collectionId: string
|
169
185
|
): Promise<void> => {
|
170
|
-
const collections = await database.listCollections(databaseId, [
|
186
|
+
const collections = await database.listCollections(databaseId, [
|
187
|
+
Query.equal("$id", collectionId),
|
188
|
+
]);
|
171
189
|
if (collections.total === 0) {
|
172
190
|
console.log(`Collection ${collectionId} not found`);
|
173
191
|
return;
|
@@ -191,7 +209,8 @@ export const createOrUpdateCollections = async (
|
|
191
209
|
deletedCollections?: { collectionId: string; collectionName: string }[],
|
192
210
|
selectedCollections: Models.Collection[] = []
|
193
211
|
): Promise<void> => {
|
194
|
-
const collectionsToProcess =
|
212
|
+
const collectionsToProcess =
|
213
|
+
selectedCollections.length > 0 ? selectedCollections : config.collections;
|
195
214
|
if (!collectionsToProcess) {
|
196
215
|
return;
|
197
216
|
}
|
@@ -204,7 +223,7 @@ export const createOrUpdateCollections = async (
|
|
204
223
|
const permissions: string[] = [];
|
205
224
|
if (collection.$permissions && collection.$permissions.length > 0) {
|
206
225
|
for (const permission of collection.$permissions) {
|
207
|
-
if (typeof permission ===
|
226
|
+
if (typeof permission === "string") {
|
208
227
|
permissions.push(permission);
|
209
228
|
} else {
|
210
229
|
switch (permission.permission) {
|
@@ -319,7 +338,7 @@ export const createOrUpdateCollections = async (
|
|
319
338
|
databaseId,
|
320
339
|
database,
|
321
340
|
collectionToUse!.$id,
|
322
|
-
(indexes ?? []) as Indexes
|
341
|
+
(indexes ?? []) as Indexes
|
323
342
|
);
|
324
343
|
|
325
344
|
// Add delay after creating indexes
|
@@ -0,0 +1,190 @@
|
|
1
|
+
import { Client, Functions, 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 { type AppwriteFunction, type Specification } from "appwrite-utils";
|
7
|
+
import chalk from "chalk";
|
8
|
+
import cliProgress from "cli-progress";
|
9
|
+
import { execSync } from "child_process";
|
10
|
+
import {
|
11
|
+
createFunction,
|
12
|
+
getFunction,
|
13
|
+
updateFunctionSpecifications,
|
14
|
+
} from "./methods.js";
|
15
|
+
|
16
|
+
const findFunctionDirectory = (
|
17
|
+
basePath: string,
|
18
|
+
functionName: string
|
19
|
+
): string | undefined => {
|
20
|
+
const normalizedName = functionName.toLowerCase().replace(/\s+/g, "-");
|
21
|
+
const dirs = fs.readdirSync(basePath, { withFileTypes: true });
|
22
|
+
|
23
|
+
for (const dir of dirs) {
|
24
|
+
if (dir.isDirectory()) {
|
25
|
+
const fullPath = join(basePath, dir.name);
|
26
|
+
if (dir.name.toLowerCase() === normalizedName) {
|
27
|
+
return fullPath;
|
28
|
+
}
|
29
|
+
|
30
|
+
const nestedResult = findFunctionDirectory(fullPath, functionName);
|
31
|
+
if (nestedResult) {
|
32
|
+
return nestedResult;
|
33
|
+
}
|
34
|
+
}
|
35
|
+
}
|
36
|
+
|
37
|
+
return undefined;
|
38
|
+
};
|
39
|
+
|
40
|
+
export const deployFunction = async (
|
41
|
+
client: Client,
|
42
|
+
functionId: string,
|
43
|
+
codePath: string,
|
44
|
+
activate: boolean = true,
|
45
|
+
entrypoint: string = "index.js",
|
46
|
+
commands: string = "npm install"
|
47
|
+
) => {
|
48
|
+
const functions = new Functions(client);
|
49
|
+
console.log(chalk.blue("📦 Preparing function deployment..."));
|
50
|
+
|
51
|
+
const progressBar = new cliProgress.SingleBar({
|
52
|
+
format:
|
53
|
+
"Uploading |" +
|
54
|
+
chalk.cyan("{bar}") +
|
55
|
+
"| {percentage}% | {value}/{total} Chunks",
|
56
|
+
barCompleteChar: "█",
|
57
|
+
barIncompleteChar: "░",
|
58
|
+
hideCursor: true,
|
59
|
+
});
|
60
|
+
|
61
|
+
const tarPath = join(process.cwd(), `function-${functionId}.tar.gz`);
|
62
|
+
await createTarball(
|
63
|
+
{
|
64
|
+
gzip: true,
|
65
|
+
file: tarPath,
|
66
|
+
cwd: codePath,
|
67
|
+
},
|
68
|
+
["."]
|
69
|
+
);
|
70
|
+
|
71
|
+
const fileBuffer = await fs.promises.readFile(tarPath);
|
72
|
+
const fileObject = InputFile.fromBuffer(
|
73
|
+
new Uint8Array(fileBuffer),
|
74
|
+
`function-${functionId}.tar.gz`
|
75
|
+
);
|
76
|
+
|
77
|
+
try {
|
78
|
+
console.log(chalk.blue("🚀 Creating deployment..."));
|
79
|
+
const functionResponse = await functions.createDeployment(
|
80
|
+
functionId,
|
81
|
+
fileObject,
|
82
|
+
activate,
|
83
|
+
entrypoint,
|
84
|
+
commands,
|
85
|
+
(progress) => {
|
86
|
+
const chunks = progress.chunksUploaded;
|
87
|
+
const total = progress.chunksTotal;
|
88
|
+
if (chunks && total) {
|
89
|
+
if (chunks === 0) {
|
90
|
+
progressBar.start(total, 0);
|
91
|
+
} else if (chunks === total) {
|
92
|
+
progressBar.update(total);
|
93
|
+
progressBar.stop();
|
94
|
+
console.log(chalk.green("✅ Upload complete!"));
|
95
|
+
} else {
|
96
|
+
progressBar.update(chunks);
|
97
|
+
}
|
98
|
+
}
|
99
|
+
}
|
100
|
+
);
|
101
|
+
|
102
|
+
await fs.promises.unlink(tarPath);
|
103
|
+
console.log(chalk.green("✨ Function deployed successfully!"));
|
104
|
+
return functionResponse;
|
105
|
+
} catch (error) {
|
106
|
+
progressBar.stop();
|
107
|
+
console.error(chalk.red("❌ Deployment failed:"), error);
|
108
|
+
throw error;
|
109
|
+
}
|
110
|
+
};
|
111
|
+
|
112
|
+
export const deployLocalFunction = async (
|
113
|
+
client: Client,
|
114
|
+
functionName: string,
|
115
|
+
functionConfig: AppwriteFunction,
|
116
|
+
functionPath?: string
|
117
|
+
) => {
|
118
|
+
let functionExists = true;
|
119
|
+
try {
|
120
|
+
await getFunction(client, functionConfig.$id);
|
121
|
+
} catch (error) {
|
122
|
+
functionExists = false;
|
123
|
+
}
|
124
|
+
|
125
|
+
const resolvedPath =
|
126
|
+
functionPath ||
|
127
|
+
functionConfig.dirPath ||
|
128
|
+
findFunctionDirectory(process.cwd(), functionName) ||
|
129
|
+
join(
|
130
|
+
process.cwd(),
|
131
|
+
"functions",
|
132
|
+
functionName.toLowerCase().replace(/\s+/g, "-")
|
133
|
+
);
|
134
|
+
|
135
|
+
if (functionConfig.predeployCommands?.length) {
|
136
|
+
console.log(chalk.blue("Executing predeploy commands..."));
|
137
|
+
for (const command of functionConfig.predeployCommands) {
|
138
|
+
try {
|
139
|
+
console.log(chalk.gray(`Executing: ${command}`));
|
140
|
+
execSync(command, {
|
141
|
+
cwd: resolvedPath,
|
142
|
+
stdio: "inherit",
|
143
|
+
});
|
144
|
+
} catch (error) {
|
145
|
+
console.error(
|
146
|
+
chalk.red(`Failed to execute predeploy command: ${command}`)
|
147
|
+
);
|
148
|
+
throw error;
|
149
|
+
}
|
150
|
+
}
|
151
|
+
}
|
152
|
+
|
153
|
+
if (!functionExists) {
|
154
|
+
await createFunction(
|
155
|
+
client,
|
156
|
+
functionConfig.$id,
|
157
|
+
functionConfig.name,
|
158
|
+
functionConfig.runtime as Runtime,
|
159
|
+
functionConfig.execute,
|
160
|
+
functionConfig.events,
|
161
|
+
functionConfig.schedule,
|
162
|
+
functionConfig.timeout,
|
163
|
+
functionConfig.enabled,
|
164
|
+
functionConfig.logging,
|
165
|
+
functionConfig.entrypoint,
|
166
|
+
functionConfig.commands
|
167
|
+
);
|
168
|
+
}
|
169
|
+
|
170
|
+
if (functionConfig.specification) {
|
171
|
+
await updateFunctionSpecifications(
|
172
|
+
client,
|
173
|
+
functionConfig.$id,
|
174
|
+
functionConfig.specification
|
175
|
+
);
|
176
|
+
}
|
177
|
+
|
178
|
+
const deployPath = functionConfig.deployDir
|
179
|
+
? join(resolvedPath, functionConfig.deployDir)
|
180
|
+
: resolvedPath;
|
181
|
+
|
182
|
+
return deployFunction(
|
183
|
+
client,
|
184
|
+
functionConfig.$id,
|
185
|
+
deployPath,
|
186
|
+
true,
|
187
|
+
functionConfig.entrypoint,
|
188
|
+
functionConfig.commands
|
189
|
+
);
|
190
|
+
};
|