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.
144 lines (143 loc) • 7.53 kB
JavaScript
import inquirer from "inquirer";
import chalk from "chalk";
import { Storage, Permission, Role, Compression } from "node-appwrite";
import { MessageFormatter } from "../../shared/messageFormatter.js";
import { listBuckets, createBucket as createBucketApi, deleteBucket as deleteBucketApi } from "../../storage/methods.js";
import { writeYamlConfig } from "../../config/yamlConfig.js";
import { ConfigManager } from "../../config/ConfigManager.js";
export const storageCommands = {
async createBucket(cli) {
const storage = cli.controller.storage;
if (!storage) {
MessageFormatter.error("Storage client not initialized", undefined, { prefix: "Buckets" });
return;
}
const answers = await inquirer.prompt([
{ type: 'input', name: 'name', message: 'Bucket name:', validate: (v) => v?.trim()?.length > 0 || 'Required' },
{ type: 'input', name: 'id', message: 'Bucket ID (optional):' },
{ type: 'confirm', name: 'publicRead', message: 'Public read access?', default: false },
{ type: 'confirm', name: 'fileSecurity', message: 'Enable file-level security?', default: false },
{ type: 'confirm', name: 'enabled', message: 'Enable bucket?', default: true },
{ type: 'number', name: 'maximumFileSize', message: 'Max file size (bytes, optional):', default: undefined },
{ type: 'input', name: 'allowedFileExtensions', message: 'Allowed extensions (comma separated, optional):', default: '' },
{ type: 'list', name: 'compression', message: 'Compression:', choices: ['none', 'gzip', 'zstd'], default: 'none' },
{ type: 'confirm', name: 'encryption', message: 'Enable encryption?', default: false },
{ type: 'confirm', name: 'antivirus', message: 'Enable antivirus?', default: false },
]);
const permissions = [];
if (answers.publicRead)
permissions.push(Permission.read(Role.any()));
const bucketInput = {
name: answers.name,
$permissions: permissions,
fileSecurity: !!answers.fileSecurity,
enabled: !!answers.enabled,
maximumFileSize: answers.maximumFileSize || undefined,
allowedFileExtensions: String(answers.allowedFileExtensions || '')
.split(',')
.map((s) => s.trim())
.filter(Boolean),
compression: answers.compression,
encryption: !!answers.encryption,
antivirus: !!answers.antivirus,
};
try {
const created = await createBucketApi(storage, bucketInput, answers.id || undefined);
MessageFormatter.success(`Bucket '${answers.name}' created`, { prefix: 'Buckets' });
// Update in-memory config and persist to YAML as a global bucket entry
const controller = cli.controller;
controller.config.buckets = controller.config.buckets || [];
controller.config.buckets.push({
$id: created.$id || answers.id || bucketInput.name,
name: bucketInput.name,
permissions: [],
fileSecurity: bucketInput.fileSecurity,
enabled: bucketInput.enabled,
maximumFileSize: bucketInput.maximumFileSize,
allowedFileExtensions: bucketInput.allowedFileExtensions,
compression: bucketInput.compression,
encryption: bucketInput.encryption,
antivirus: bucketInput.antivirus,
});
const cfgMgr = ConfigManager.getInstance();
const cfgPath = cfgMgr.getConfigPath();
if (cfgPath && /\.ya?ml$/i.test(cfgPath)) {
await writeYamlConfig(cfgPath, controller.config);
MessageFormatter.info(`Added bucket to config.yaml`, { prefix: 'Buckets' });
}
else {
MessageFormatter.warning(`Config is not YAML; updated in-memory only. Please update your TypeScript config manually.`, { prefix: 'Buckets' });
}
}
catch (e) {
MessageFormatter.error('Failed to create bucket', e instanceof Error ? e : new Error(String(e)), { prefix: 'Buckets' });
}
},
async deleteBuckets(cli) {
const storage = cli.controller.storage;
if (!storage) {
MessageFormatter.error("Storage client not initialized", undefined, { prefix: "Buckets" });
return;
}
try {
const res = await listBuckets(storage);
const buckets = res.buckets || [];
if (buckets.length === 0) {
MessageFormatter.info('No buckets found', { prefix: 'Buckets' });
return;
}
const { toDelete } = await inquirer.prompt([
{
type: 'checkbox',
name: 'toDelete',
message: chalk.red('Select buckets to delete:'),
choices: buckets.map((b) => ({ name: `${b.name} (${b.$id})`, value: b.$id })),
pageSize: 10,
}
]);
if (!toDelete || toDelete.length === 0) {
MessageFormatter.info('No buckets selected', { prefix: 'Buckets' });
return;
}
const { confirm } = await inquirer.prompt([
{ type: 'confirm', name: 'confirm', message: `Delete ${toDelete.length} bucket(s)?`, default: false }
]);
if (!confirm)
return;
const controller = cli.controller;
for (const id of toDelete) {
try {
await deleteBucketApi(storage, id);
MessageFormatter.success(`Deleted bucket ${id}`, { prefix: 'Buckets' });
// Remove from in-memory config (global buckets)
if (Array.isArray(controller.config.buckets)) {
controller.config.buckets = controller.config.buckets.filter((b) => b.$id !== id);
}
// Clear database-linked bucket references if matching
if (Array.isArray(controller.config.databases)) {
controller.config.databases = controller.config.databases.map((db) => ({
...db,
bucket: db.bucket && db.bucket.$id === id ? undefined : db.bucket,
}));
}
}
catch (e) {
MessageFormatter.error(`Failed to delete bucket ${id}`, e instanceof Error ? e : new Error(String(e)), { prefix: 'Buckets' });
}
}
// Persist YAML changes
const cfgMgr = ConfigManager.getInstance();
const cfgPath = cfgMgr.getConfigPath();
if (cfgPath && /\.ya?ml$/i.test(cfgPath)) {
await writeYamlConfig(cfgPath, controller.config);
MessageFormatter.info(`Updated config.yaml after deletion`, { prefix: 'Buckets' });
}
else {
MessageFormatter.warning(`Config is not YAML; updated in-memory only. Please update your TypeScript config manually.`, { prefix: 'Buckets' });
}
}
catch (e) {
MessageFormatter.error('Failed to list buckets', e instanceof Error ? e : new Error(String(e)), { prefix: 'Buckets' });
}
}
};