appwrite-utils-cli
Version:
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.
147 lines (146 loc) • 7.02 kB
JavaScript
import { AppwriteException, Client, Functions, Query, Runtime, } from "node-appwrite";
import { join, dirname } from "node:path";
import { fileURLToPath } from "node:url";
import fs from "node:fs";
import {} from "appwrite-utils";
import chalk from "chalk";
import { extract as extractTar } from "tar";
export const listFunctions = async (client, queries, search) => {
const functions = new Functions(client);
const functionsList = await functions.list(queries, search);
return functionsList;
};
export const getFunction = async (client, functionId) => {
const functions = new Functions(client);
const functionResponse = await functions.get(functionId);
return functionResponse;
};
export const downloadLatestFunctionDeployment = async (client, functionId, basePath = process.cwd()) => {
const functions = new Functions(client);
const functionInfo = await getFunction(client, functionId);
const functionDeployments = await functions.listDeployments(functionId, [
Query.orderDesc("$createdAt"),
]);
if (functionDeployments.deployments.length === 0) {
throw new Error("No deployments found for function");
}
const latestDeployment = functionDeployments.deployments[0];
const deploymentData = await functions.getDeploymentDownload(functionId, latestDeployment.$id);
// Create function directory using provided basePath
const functionDir = join(basePath, functionInfo.name.toLowerCase().replace(/\s+/g, "-"));
await fs.promises.mkdir(functionDir, { recursive: true });
// Create temporary file for tar extraction
const tarPath = join(functionDir, "temp.tar.gz");
const uint8Array = new Uint8Array(deploymentData);
await fs.promises.writeFile(tarPath, uint8Array);
try {
// Extract tar file
extractTar({
C: functionDir,
file: tarPath,
sync: true,
});
return {
path: functionDir,
function: functionInfo,
deployment: latestDeployment,
};
}
finally {
// Clean up tar file
await fs.promises.unlink(tarPath).catch(() => { });
}
};
export const deleteFunction = async (client, functionId) => {
const functions = new Functions(client);
const functionResponse = await functions.delete(functionId);
return functionResponse;
};
export const createFunction = async (client, functionConfig) => {
const functions = new Functions(client);
const functionResponse = await functions.create(functionConfig.$id, functionConfig.name, functionConfig.runtime, functionConfig.execute, functionConfig.events, functionConfig.schedule, functionConfig.timeout, functionConfig.enabled, functionConfig.logging, functionConfig.entrypoint, functionConfig.commands, functionConfig.scopes, functionConfig.installationId, functionConfig.providerRepositoryId, functionConfig.providerBranch, functionConfig.providerSilentMode, functionConfig.providerRootDirectory);
return functionResponse;
};
export const updateFunctionSpecifications = async (client, functionId, specification) => {
const curFunction = await listFunctions(client, [
Query.equal("$id", functionId),
]);
if (curFunction.functions.length === 0) {
throw new Error("Function not found");
}
const functionFound = curFunction.functions[0];
try {
const functionResponse = await updateFunction(client, {
...functionFound,
runtime: functionFound.runtime,
scopes: functionFound.scopes,
specification: specification,
});
return functionResponse;
}
catch (error) {
if (error instanceof AppwriteException &&
error.message.includes("Invalid `specification`")) {
console.error(chalk.red("Error updating function specifications, please try setting the env variable `_FUNCTIONS_CPUS` and `_FUNCTIONS_RAM` to non-zero values"));
}
else {
console.error(chalk.red("Error updating function specifications."));
throw error;
}
}
};
export const listSpecifications = async (client) => {
const functions = new Functions(client);
const specifications = await functions.listSpecifications();
return specifications;
};
export const listFunctionDeployments = async (client, functionId, queries) => {
const functions = new Functions(client);
const deployments = await functions.listDeployments(functionId, queries);
return deployments;
};
export const updateFunction = async (client, functionConfig) => {
const functions = new Functions(client);
const functionResponse = await functions.update(functionConfig.$id, functionConfig.name, functionConfig.runtime, functionConfig.execute, functionConfig.events, functionConfig.schedule, functionConfig.timeout, functionConfig.enabled, functionConfig.logging, functionConfig.entrypoint, functionConfig.commands, functionConfig.scopes, functionConfig.installationId, functionConfig.providerRepositoryId, functionConfig.providerBranch, functionConfig.providerSilentMode, functionConfig.providerRootDirectory, functionConfig.specification);
return functionResponse;
};
export const createFunctionTemplate = async (templateType, functionName, basePath = "./functions") => {
const functionPath = join(basePath, functionName);
const currentFileUrl = import.meta.url;
const currentDir = dirname(fileURLToPath(currentFileUrl));
const templatesPath = join(currentDir, "templates", templateType);
// Create function directory
await fs.promises.mkdir(functionPath, { recursive: true });
// Copy template files recursively
const copyTemplateFiles = async (sourcePath, targetPath) => {
const entries = await fs.promises.readdir(sourcePath, {
withFileTypes: true,
});
for (const entry of entries) {
const srcPath = join(sourcePath, entry.name);
const destPath = join(targetPath, entry.name);
if (entry.isDirectory()) {
await fs.promises.mkdir(destPath, { recursive: true });
await copyTemplateFiles(srcPath, destPath);
}
else {
let content = await fs.promises.readFile(srcPath, "utf-8");
// Replace template variables
content = content
.replace(/\{\{functionName\}\}/g, functionName)
.replace(/\{\{databaseId\}\}/g, "{{databaseId}}")
.replace(/\{\{collectionId\}\}/g, "{{collectionId}}");
await fs.promises.writeFile(destPath, content);
}
}
};
try {
await copyTemplateFiles(templatesPath, functionPath);
console.log(chalk.green(`✨ Created ${templateType} function template at ${functionPath}`));
}
catch (error) {
console.error(chalk.red(`Failed to create function template: ${error}`));
throw error;
}
return functionPath;
};