UNPKG

@crazyrabbitltc/railway-mcp

Version:

Railway MCP Server - 146+ tools with 100% Railway API coverage, comprehensive MCP testing framework, and real infrastructure management through AI assistants. Enhanced version with enterprise features, based on original work by Jason Tan.

289 lines (288 loc) 11.9 kB
import { BaseService } from "./base.service.js"; import { createSuccessResponse, createErrorResponse, formatError } from "../utils/responses.js"; export class BackupService extends BaseService { constructor() { super(); } async listBackups(projectId) { try { const backups = await this.client.backup.listBackups(projectId); const completedCount = backups.filter(b => b.status === 'COMPLETED').length; const inProgressCount = backups.filter(b => b.status === 'IN_PROGRESS').length; const failedCount = backups.filter(b => b.status === 'FAILED').length; const totalSize = backups .filter(b => b.size) .reduce((sum, b) => sum + (b.size || 0), 0); const byType = backups.reduce((acc, backup) => { if (!acc[backup.type]) acc[backup.type] = []; acc[backup.type].push(backup); return acc; }, {}); return createSuccessResponse({ text: `Found ${backups.length} backups (${completedCount} completed, ${inProgressCount} in progress, ${failedCount} failed)`, data: { projectId, summary: { totalCount: backups.length, completedCount, inProgressCount, failedCount, totalSizeGB: (totalSize / (1024 * 1024 * 1024)).toFixed(2) }, byType: Object.entries(byType).map(([type, typeBackups]) => ({ type, count: typeBackups.length, latestBackup: typeBackups.sort((a, b) => new Date(b.createdAt).getTime() - new Date(a.createdAt).getTime())[0]?.createdAt })), backups: backups.map(backup => ({ id: backup.id, type: backup.type, status: backup.status, size: backup.size ? `${(backup.size / (1024 * 1024)).toFixed(2)} MB` : 'N/A', createdAt: backup.createdAt, completedAt: backup.completedAt, expiresAt: backup.expiresAt, description: backup.metadata?.description || 'No description', serviceId: backup.serviceId, volumeId: backup.volumeId })) } }); } catch (error) { return createErrorResponse(`Failed to list backups: ${formatError(error)}`); } } async createBackup(projectId, type, serviceId, volumeId, description, retentionDays) { try { const backup = await this.client.backup.createBackup({ projectId, type: type, serviceId, volumeId, description, retentionDays: retentionDays || 30 }); return createSuccessResponse({ text: `${type} backup created successfully`, data: { id: backup.id, type: backup.type, status: backup.status, projectId: backup.projectId, serviceId: backup.serviceId, volumeId: backup.volumeId, description: backup.metadata?.description, retentionDays: backup.metadata?.retentionDays, createdAt: backup.createdAt } }); } catch (error) { return createErrorResponse(`Failed to create backup: ${formatError(error)}`); } } async getBackup(backupId) { try { const backup = await this.client.backup.getBackup(backupId); const progress = backup.status === 'COMPLETED' ? 100 : backup.status === 'IN_PROGRESS' ? 50 : 0; return createSuccessResponse({ text: `Backup details for ${backup.type} backup`, data: { id: backup.id, type: backup.type, status: backup.status, progress: `${progress}%`, projectId: backup.projectId, serviceId: backup.serviceId, volumeId: backup.volumeId, size: backup.size ? `${(backup.size / (1024 * 1024)).toFixed(2)} MB` : 'N/A', metadata: { description: backup.metadata?.description || 'No description', retentionDays: backup.metadata?.retentionDays || 30, tags: backup.metadata?.tags || [], compressionType: backup.metadata?.compressionType || 'gzip' }, timing: { createdAt: backup.createdAt, completedAt: backup.completedAt || 'N/A', expiresAt: backup.expiresAt || 'N/A' } } }); } catch (error) { return createErrorResponse(`Failed to get backup: ${formatError(error)}`); } } async restoreBackup(backupId, targetProjectId, targetServiceId, overwrite) { try { const restore = await this.client.backup.restoreBackup({ backupId, targetProjectId, targetServiceId, options: { overwrite: overwrite || false, excludeVolumes: false } }); return createSuccessResponse({ text: `Restore operation initiated`, data: { restoreId: restore.id, backupId: restore.backupId, targetProjectId: restore.targetProjectId, targetServiceId: restore.targetServiceId, status: restore.status, progress: `${restore.progress || 0}%`, createdAt: restore.createdAt } }); } catch (error) { return createErrorResponse(`Failed to restore backup: ${formatError(error)}`); } } async getRestoreStatus(restoreId) { try { const restore = await this.client.backup.getRestoreStatus(restoreId); return createSuccessResponse({ text: `Restore operation is ${restore.status.toLowerCase()}`, data: { id: restore.id, backupId: restore.backupId, status: restore.status, progress: `${restore.progress || 0}%`, targetProjectId: restore.targetProjectId, targetServiceId: restore.targetServiceId, timing: { createdAt: restore.createdAt, completedAt: restore.completedAt || 'N/A' }, errorMessage: restore.errorMessage || null } }); } catch (error) { return createErrorResponse(`Failed to get restore status: ${formatError(error)}`); } } async deleteBackup(backupId) { try { const success = await this.client.backup.deleteBackup(backupId); if (success) { return createSuccessResponse({ text: "Backup deleted successfully" }); } else { return createErrorResponse("Failed to delete backup"); } } catch (error) { return createErrorResponse(`Failed to delete backup: ${formatError(error)}`); } } async listBackupPolicies(projectId) { try { const policies = await this.client.backup.listBackupPolicies(projectId); const activeCount = policies.filter(p => p.isActive).length; const inactiveCount = policies.length - activeCount; return createSuccessResponse({ text: `Found ${policies.length} backup policies (${activeCount} active, ${inactiveCount} inactive)`, data: { projectId, summary: { totalCount: policies.length, activeCount, inactiveCount }, policies: policies.map(policy => ({ id: policy.id, name: policy.name, schedule: policy.schedule, backupType: policy.backupType, retentionDays: policy.retentionDays, isActive: policy.isActive, targetCount: policy.targets.length, createdAt: policy.createdAt, updatedAt: policy.updatedAt })) } }); } catch (error) { return createErrorResponse(`Failed to list backup policies: ${formatError(error)}`); } } async createBackupPolicy(projectId, name, schedule, backupType, retentionDays, targets) { try { const policy = await this.client.backup.createBackupPolicy({ projectId, name, schedule, backupType: backupType, retentionDays, targets }); return createSuccessResponse({ text: `Backup policy "${name}" created successfully`, data: { id: policy.id, name: policy.name, schedule: policy.schedule, backupType: policy.backupType, retentionDays: policy.retentionDays, isActive: policy.isActive, targetCount: policy.targets.length, createdAt: policy.createdAt } }); } catch (error) { return createErrorResponse(`Failed to create backup policy: ${formatError(error)}`); } } async updateBackupPolicy(policyId, name, schedule, retentionDays, isActive) { try { const policy = await this.client.backup.updateBackupPolicy(policyId, { name, schedule, retentionDays }); return createSuccessResponse({ text: `Backup policy updated successfully`, data: { id: policy.id, name: policy.name, schedule: policy.schedule, retentionDays: policy.retentionDays, isActive: policy.isActive, updatedAt: policy.updatedAt } }); } catch (error) { return createErrorResponse(`Failed to update backup policy: ${formatError(error)}`); } } async deleteBackupPolicy(policyId) { try { const success = await this.client.backup.deleteBackupPolicy(policyId); if (success) { return createSuccessResponse({ text: "Backup policy deleted successfully" }); } else { return createErrorResponse("Failed to delete backup policy"); } } catch (error) { return createErrorResponse(`Failed to delete backup policy: ${formatError(error)}`); } } } export const backupService = new BackupService();