@aerocorp/cli
Version:
AeroCorp CLI 5.1.0 - Future-Proofed Enterprise Infrastructure with Live Preview, Tunneling & Advanced DevOps
313 lines (261 loc) ⢠10.4 kB
text/typescript
/**
* AeroCorp CLI 4.0.0 - Database Management Service
* Comprehensive database operations for hybrid infrastructure
*/
import axios from 'axios';
import chalk from 'chalk';
import inquirer from 'inquirer';
import { ConfigService } from './config';
import { AuthService } from './auth';
export interface DatabaseInstance {
id: string;
name: string;
type: 'postgresql' | 'mysql' | 'mongodb' | 'redis';
status: 'running' | 'stopped' | 'maintenance';
version: string;
size: string;
connections: number;
maxConnections: number;
lastBackup: string;
platform: 'coolify' | 'caprover';
url: string;
}
export interface BackupInfo {
id: string;
databaseId: string;
database: string;
size: string;
timestamp: string;
type: 'automatic' | 'manual';
status: 'completed' | 'failed' | 'in_progress';
location: string;
}
export class DatabaseService {
private configService: ConfigService;
private authService: AuthService;
constructor() {
this.configService = new ConfigService();
this.authService = new AuthService();
}
async listDatabases(options: { platform?: string; type?: string; status?: string } = {}): Promise<void> {
try {
const config = await this.configService.getConfig();
const auth = await this.authService.getAuth();
const params = new URLSearchParams();
if (options.platform) params.append('platform', options.platform);
if (options.type) params.append('type', options.type);
if (options.status) params.append('status', options.status);
const response = await axios.get(`${config.apiUrl}/api/databases?${params}`, {
headers: {
'Authorization': `Bearer ${auth.token}`,
'Content-Type': 'application/json'
},
timeout: 10000
});
const data = response.data;
console.log(chalk.cyan.bold('\nš¾ Database Instances'));
console.log(chalk.gray(`Total: ${data.total} databases`));
console.log(chalk.gray(`Running: ${data.summary.running} | Stopped: ${data.summary.stopped} | Maintenance: ${data.summary.maintenance}`));
if (data.databases.length === 0) {
console.log(chalk.yellow('No databases found'));
return;
}
console.log('\n' + chalk.white('Name'.padEnd(25) + 'Type'.padEnd(12) + 'Status'.padEnd(12) + 'Platform'.padEnd(12) + 'Size'.padEnd(10) + 'Connections'));
console.log(chalk.gray('ā'.repeat(80)));
data.databases.forEach((db: DatabaseInstance) => {
const statusIcon = db.status === 'running' ? 'ā
' :
db.status === 'maintenance' ? 'ā ļø' : 'ā';
const statusColor = db.status === 'running' ? chalk.green :
db.status === 'maintenance' ? chalk.yellow : chalk.red;
console.log(
`${statusIcon} ${chalk.white(db.name.padEnd(22))} ` +
`${chalk.blue(db.type.padEnd(11))} ` +
`${statusColor(db.status.padEnd(11))} ` +
`${chalk.cyan(db.platform.padEnd(11))} ` +
`${chalk.white(db.size.padEnd(9))} ` +
`${chalk.white(db.connections + '/' + db.maxConnections)}`
);
});
} catch (error) {
throw new Error(`Failed to list databases: ${error.message}`);
}
}
async createDatabase(options: any = {}): Promise<void> {
try {
let dbConfig;
if (options.interactive !== false) {
dbConfig = await inquirer.prompt([
{
type: 'input',
name: 'name',
message: 'Database name:',
default: options.name,
validate: (input) => input.length > 0 || 'Name is required'
},
{
type: 'list',
name: 'type',
message: 'Database type:',
choices: ['postgresql', 'mysql', 'mongodb', 'redis'],
default: options.type || 'postgresql'
},
{
type: 'list',
name: 'platform',
message: 'Deployment platform:',
choices: ['coolify', 'caprover'],
default: options.platform || 'coolify'
},
{
type: 'input',
name: 'version',
message: 'Version (leave empty for latest):',
default: options.version || ''
},
{
type: 'input',
name: 'initialSize',
message: 'Initial storage size:',
default: options.initialSize || '1GB'
}
]);
} else {
dbConfig = options;
}
const config = await this.configService.getConfig();
const auth = await this.authService.getAuth();
console.log(chalk.blue('š Creating database...'));
const response = await axios.post(`${config.apiUrl}/api/databases`, dbConfig, {
headers: {
'Authorization': `Bearer ${auth.token}`,
'Content-Type': 'application/json'
},
timeout: 30000
});
const database = response.data.database;
console.log(chalk.green('ā
Database created successfully!'));
console.log(chalk.white(`Name: ${database.name}`));
console.log(chalk.white(`Type: ${database.type}`));
console.log(chalk.white(`Platform: ${database.platform}`));
console.log(chalk.white(`URL: ${database.url}`));
} catch (error) {
throw new Error(`Failed to create database: ${error.message}`);
}
}
async backupDatabase(databaseId: string, options: { type?: string } = {}): Promise<void> {
try {
const config = await this.configService.getConfig();
const auth = await this.authService.getAuth();
console.log(chalk.blue('š Starting database backup...'));
const response = await axios.post(`${config.apiUrl}/api/databases/${databaseId}/backup`, {
type: options.type || 'manual'
}, {
headers: {
'Authorization': `Bearer ${auth.token}`,
'Content-Type': 'application/json'
},
timeout: 30000
});
const backup = response.data.backup;
console.log(chalk.green('ā
Backup started successfully!'));
console.log(chalk.white(`Backup ID: ${backup.id}`));
console.log(chalk.white(`Database: ${backup.database}`));
console.log(chalk.white(`Type: ${backup.type}`));
console.log(chalk.white(`Status: ${backup.status}`));
} catch (error) {
throw new Error(`Failed to backup database: ${error.message}`);
}
}
async restoreDatabase(databaseId: string, backupId: string): Promise<void> {
try {
const config = await this.configService.getConfig();
const auth = await this.authService.getAuth();
console.log(chalk.blue('š Starting database restore...'));
const response = await axios.post(`${config.apiUrl}/api/databases/${databaseId}/restore`, {
backupId
}, {
headers: {
'Authorization': `Bearer ${auth.token}`,
'Content-Type': 'application/json'
},
timeout: 60000
});
console.log(chalk.green('ā
Restore started successfully!'));
console.log(chalk.yellow('ā ļø Database will be in maintenance mode during restore'));
} catch (error) {
throw new Error(`Failed to restore database: ${error.message}`);
}
}
async listBackups(databaseId?: string): Promise<void> {
try {
const config = await this.configService.getConfig();
const auth = await this.authService.getAuth();
const url = databaseId
? `${config.apiUrl}/api/databases/${databaseId}/backups`
: `${config.apiUrl}/api/databases/backups`;
const response = await axios.get(url, {
headers: {
'Authorization': `Bearer ${auth.token}`,
'Content-Type': 'application/json'
},
timeout: 10000
});
const data = response.data;
console.log(chalk.cyan.bold('\nš¾ Database Backups'));
console.log(chalk.gray(`Total: ${data.total} backups`));
if (data.backups.length === 0) {
console.log(chalk.yellow('No backups found'));
return;
}
console.log('\n' + chalk.white('Database'.padEnd(25) + 'Size'.padEnd(10) + 'Type'.padEnd(12) + 'Status'.padEnd(12) + 'Date'));
console.log(chalk.gray('ā'.repeat(80)));
data.backups.forEach((backup: BackupInfo) => {
const statusIcon = backup.status === 'completed' ? 'ā
' :
backup.status === 'in_progress' ? 'š' : 'ā';
const statusColor = backup.status === 'completed' ? chalk.green :
backup.status === 'in_progress' ? chalk.blue : chalk.red;
console.log(
`${statusIcon} ${chalk.white(backup.database.padEnd(22))} ` +
`${chalk.white(backup.size.padEnd(9))} ` +
`${chalk.cyan(backup.type.padEnd(11))} ` +
`${statusColor(backup.status.padEnd(11))} ` +
`${chalk.gray(new Date(backup.timestamp).toLocaleDateString())}`
);
});
} catch (error) {
throw new Error(`Failed to list backups: ${error.message}`);
}
}
async deleteDatabase(databaseId: string, options: { force?: boolean } = {}): Promise<void> {
try {
if (!options.force) {
const confirm = await inquirer.prompt([
{
type: 'confirm',
name: 'confirmed',
message: 'Are you sure you want to delete this database? This action cannot be undone.',
default: false
}
]);
if (!confirm.confirmed) {
console.log(chalk.yellow('ā Database deletion cancelled'));
return;
}
}
const config = await this.configService.getConfig();
const auth = await this.authService.getAuth();
console.log(chalk.blue('š Deleting database...'));
const response = await axios.delete(`${config.apiUrl}/api/databases/${databaseId}`, {
headers: {
'Authorization': `Bearer ${auth.token}`,
'Content-Type': 'application/json'
},
timeout: 30000
});
console.log(chalk.green('ā
Database deleted successfully!'));
console.log(chalk.gray(`Removed ${response.data.removedBackups} associated backups`));
} catch (error) {
throw new Error(`Failed to delete database: ${error.message}`);
}
}
}