388 lines
		
	
	
		
			11 KiB
		
	
	
	
		
			TypeScript
		
	
	
	
	
	
			
		
		
	
	
			388 lines
		
	
	
		
			11 KiB
		
	
	
	
		
			TypeScript
		
	
	
	
	
	
| /**
 | |
|  * Mailer CLI
 | |
|  * Main command-line interface implementation
 | |
|  */
 | |
| 
 | |
| import { DaemonManager } from '../daemon/daemon-manager.ts';
 | |
| import { ConfigManager } from '../config/config-manager.ts';
 | |
| import { DnsManager } from '../dns/dns-manager.ts';
 | |
| import { CloudflareClient } from '../dns/cloudflare-client.ts';
 | |
| import { Email } from '../mail/core/index.ts';
 | |
| import { commitinfo } from '../00_commitinfo_data.ts';
 | |
| 
 | |
| export class MailerCli {
 | |
|   private configManager: ConfigManager;
 | |
|   private daemonManager: DaemonManager;
 | |
|   private dnsManager: DnsManager;
 | |
| 
 | |
|   constructor() {
 | |
|     this.configManager = new ConfigManager();
 | |
|     this.daemonManager = new DaemonManager();
 | |
|     this.dnsManager = new DnsManager();
 | |
|   }
 | |
| 
 | |
|   /**
 | |
|    * Parse and execute CLI commands
 | |
|    */
 | |
|   async parseAndExecute(args: string[]): Promise<void> {
 | |
|     // Get command
 | |
|     const command = args[2] || 'help';
 | |
|     const subcommand = args[3];
 | |
|     const commandArgs = args.slice(4);
 | |
| 
 | |
|     try {
 | |
|       switch (command) {
 | |
|         case 'service':
 | |
|           await this.handleServiceCommand(subcommand, commandArgs);
 | |
|           break;
 | |
| 
 | |
|         case 'domain':
 | |
|           await this.handleDomainCommand(subcommand, commandArgs);
 | |
|           break;
 | |
| 
 | |
|         case 'dns':
 | |
|           await this.handleDnsCommand(subcommand, commandArgs);
 | |
|           break;
 | |
| 
 | |
|         case 'send':
 | |
|           await this.handleSendCommand(commandArgs);
 | |
|           break;
 | |
| 
 | |
|         case 'config':
 | |
|           await this.handleConfigCommand(subcommand, commandArgs);
 | |
|           break;
 | |
| 
 | |
|         case 'version':
 | |
|         case '--version':
 | |
|         case '-v':
 | |
|           this.showVersion();
 | |
|           break;
 | |
| 
 | |
|         case 'help':
 | |
|         case '--help':
 | |
|         case '-h':
 | |
|         default:
 | |
|           this.showHelp();
 | |
|           break;
 | |
|       }
 | |
|     } catch (error) {
 | |
|       console.error(`Error: ${error.message}`);
 | |
|       Deno.exit(1);
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   /**
 | |
|    * Handle service commands (daemon control)
 | |
|    */
 | |
|   private async handleServiceCommand(subcommand: string, args: string[]): Promise<void> {
 | |
|     switch (subcommand) {
 | |
|       case 'start':
 | |
|         console.log('Starting mailer daemon...');
 | |
|         await this.daemonManager.start();
 | |
|         break;
 | |
| 
 | |
|       case 'stop':
 | |
|         console.log('Stopping mailer daemon...');
 | |
|         await this.daemonManager.stop();
 | |
|         break;
 | |
| 
 | |
|       case 'restart':
 | |
|         console.log('Restarting mailer daemon...');
 | |
|         await this.daemonManager.stop();
 | |
|         await new Promise(resolve => setTimeout(resolve, 2000));
 | |
|         await this.daemonManager.start();
 | |
|         break;
 | |
| 
 | |
|       case 'status':
 | |
|         console.log('Checking mailer daemon status...');
 | |
|         // TODO: Implement status check
 | |
|         break;
 | |
| 
 | |
|       case 'enable':
 | |
|         console.log('Enabling mailer service (systemd)...');
 | |
|         // TODO: Implement systemd enable
 | |
|         break;
 | |
| 
 | |
|       case 'disable':
 | |
|         console.log('Disabling mailer service (systemd)...');
 | |
|         // TODO: Implement systemd disable
 | |
|         break;
 | |
| 
 | |
|       default:
 | |
|         console.log('Usage: mailer service {start|stop|restart|status|enable|disable}');
 | |
|         break;
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   /**
 | |
|    * Handle domain management commands
 | |
|    */
 | |
|   private async handleDomainCommand(subcommand: string, args: string[]): Promise<void> {
 | |
|     const config = await this.configManager.load();
 | |
| 
 | |
|     switch (subcommand) {
 | |
|       case 'add': {
 | |
|         const domain = args[0];
 | |
|         if (!domain) {
 | |
|           console.error('Error: Domain name required');
 | |
|           console.log('Usage: mailer domain add <domain>');
 | |
|           Deno.exit(1);
 | |
|         }
 | |
| 
 | |
|         config.domains.push({
 | |
|           domain,
 | |
|           dnsMode: 'external-dns',
 | |
|         });
 | |
| 
 | |
|         await this.configManager.save(config);
 | |
|         console.log(`✓ Domain ${domain} added`);
 | |
|         break;
 | |
|       }
 | |
| 
 | |
|       case 'remove': {
 | |
|         const domain = args[0];
 | |
|         if (!domain) {
 | |
|           console.error('Error: Domain name required');
 | |
|           console.log('Usage: mailer domain remove <domain>');
 | |
|           Deno.exit(1);
 | |
|         }
 | |
| 
 | |
|         config.domains = config.domains.filter(d => d.domain !== domain);
 | |
|         await this.configManager.save(config);
 | |
|         console.log(`✓ Domain ${domain} removed`);
 | |
|         break;
 | |
|       }
 | |
| 
 | |
|       case 'list':
 | |
|         console.log('Configured domains:');
 | |
|         if (config.domains.length === 0) {
 | |
|           console.log('  (none)');
 | |
|         } else {
 | |
|           for (const domain of config.domains) {
 | |
|             console.log(`  - ${domain.domain} (${domain.dnsMode})`);
 | |
|           }
 | |
|         }
 | |
|         break;
 | |
| 
 | |
|       default:
 | |
|         console.log('Usage: mailer domain {add|remove|list} [domain]');
 | |
|         break;
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   /**
 | |
|    * Handle DNS commands
 | |
|    */
 | |
|   private async handleDnsCommand(subcommand: string, args: string[]): Promise<void> {
 | |
|     const domain = args[0];
 | |
| 
 | |
|     if (!domain && subcommand !== 'help') {
 | |
|       console.error('Error: Domain name required');
 | |
|       console.log('Usage: mailer dns {setup|validate|show} <domain>');
 | |
|       Deno.exit(1);
 | |
|     }
 | |
| 
 | |
|     switch (subcommand) {
 | |
|       case 'setup': {
 | |
|         console.log(`Setting up DNS for ${domain}...`);
 | |
| 
 | |
|         const config = await this.configManager.load();
 | |
|         const domainConfig = config.domains.find(d => d.domain === domain);
 | |
| 
 | |
|         if (!domainConfig) {
 | |
|           console.error(`Error: Domain ${domain} not configured. Add it first with: mailer domain add ${domain}`);
 | |
|           Deno.exit(1);
 | |
|         }
 | |
| 
 | |
|         if (!domainConfig.cloudflare?.apiToken) {
 | |
|           console.error('Error: Cloudflare API token not configured');
 | |
|           console.log('Set it with: mailer config set cloudflare.apiToken <token>');
 | |
|           Deno.exit(1);
 | |
|         }
 | |
| 
 | |
|         const cloudflare = new CloudflareClient({ apiToken: domainConfig.cloudflare.apiToken });
 | |
|         const records = this.dnsManager.getRequiredRecords(domain, config.hostname);
 | |
|         await cloudflare.createRecords(domain, records);
 | |
| 
 | |
|         console.log(`✓ DNS records created for ${domain}`);
 | |
|         break;
 | |
|       }
 | |
| 
 | |
|       case 'validate': {
 | |
|         console.log(`Validating DNS for ${domain}...`);
 | |
|         const result = await this.dnsManager.validateDomain(domain);
 | |
| 
 | |
|         if (result.valid) {
 | |
|           console.log(`✓ DNS configuration is valid`);
 | |
|         } else {
 | |
|           console.log(`✗ DNS configuration has errors:`);
 | |
|           for (const error of result.errors) {
 | |
|             console.log(`  - ${error}`);
 | |
|           }
 | |
|         }
 | |
| 
 | |
|         if (result.warnings.length > 0) {
 | |
|           console.log('Warnings:');
 | |
|           for (const warning of result.warnings) {
 | |
|             console.log(`  - ${warning}`);
 | |
|           }
 | |
|         }
 | |
|         break;
 | |
|       }
 | |
| 
 | |
|       case 'show': {
 | |
|         console.log(`Required DNS records for ${domain}:`);
 | |
|         const config = await this.configManager.load();
 | |
|         const records = this.dnsManager.getRequiredRecords(domain, config.hostname);
 | |
| 
 | |
|         for (const record of records) {
 | |
|           console.log(`\n${record.type} Record:`);
 | |
|           console.log(`  Name: ${record.name}`);
 | |
|           console.log(`  Value: ${record.value}`);
 | |
|           if (record.priority) console.log(`  Priority: ${record.priority}`);
 | |
|           if (record.ttl) console.log(`  TTL: ${record.ttl}`);
 | |
|         }
 | |
|         break;
 | |
|       }
 | |
| 
 | |
|       default:
 | |
|         console.log('Usage: mailer dns {setup|validate|show} <domain>');
 | |
|         break;
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   /**
 | |
|    * Handle send command
 | |
|    */
 | |
|   private async handleSendCommand(args: string[]): Promise<void> {
 | |
|     console.log('Sending email...');
 | |
| 
 | |
|     // Parse basic arguments
 | |
|     const from = args[args.indexOf('--from') + 1];
 | |
|     const to = args[args.indexOf('--to') + 1];
 | |
|     const subject = args[args.indexOf('--subject') + 1];
 | |
|     const text = args[args.indexOf('--text') + 1];
 | |
| 
 | |
|     if (!from || !to || !subject || !text) {
 | |
|       console.error('Error: Missing required arguments');
 | |
|       console.log('Usage: mailer send --from <email> --to <email> --subject <subject> --text <text>');
 | |
|       Deno.exit(1);
 | |
|     }
 | |
| 
 | |
|     const email = new Email({
 | |
|       from,
 | |
|       to,
 | |
|       subject,
 | |
|       text,
 | |
|     });
 | |
| 
 | |
|     console.log(`✓ Email created: ${email.toString()}`);
 | |
|     // TODO: Actually send the email via SMTP client
 | |
|     console.log('TODO: Implement actual sending');
 | |
|   }
 | |
| 
 | |
|   /**
 | |
|    * Handle config commands
 | |
|    */
 | |
|   private async handleConfigCommand(subcommand: string, args: string[]): Promise<void> {
 | |
|     const config = await this.configManager.load();
 | |
| 
 | |
|     switch (subcommand) {
 | |
|       case 'show':
 | |
|         console.log('Current configuration:');
 | |
|         console.log(JSON.stringify(config, null, 2));
 | |
|         break;
 | |
| 
 | |
|       case 'set': {
 | |
|         const key = args[0];
 | |
|         const value = args[1];
 | |
| 
 | |
|         if (!key || !value) {
 | |
|           console.error('Error: Key and value required');
 | |
|           console.log('Usage: mailer config set <key> <value>');
 | |
|           Deno.exit(1);
 | |
|         }
 | |
| 
 | |
|         // Simple key-value setting (can be enhanced)
 | |
|         if (key === 'smtpPort') config.smtpPort = parseInt(value);
 | |
|         else if (key === 'apiPort') config.apiPort = parseInt(value);
 | |
|         else if (key === 'hostname') config.hostname = value;
 | |
|         else {
 | |
|           console.error(`Error: Unknown config key: ${key}`);
 | |
|           Deno.exit(1);
 | |
|         }
 | |
| 
 | |
|         await this.configManager.save(config);
 | |
|         console.log(`✓ Configuration updated: ${key} = ${value}`);
 | |
|         break;
 | |
|       }
 | |
| 
 | |
|       default:
 | |
|         console.log('Usage: mailer config {show|set} [key] [value]');
 | |
|         break;
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   /**
 | |
|    * Show version information
 | |
|    */
 | |
|   private showVersion(): void {
 | |
|     console.log(`${commitinfo.name} v${commitinfo.version}`);
 | |
|     console.log(commitinfo.description);
 | |
|   }
 | |
| 
 | |
|   /**
 | |
|    * Show help information
 | |
|    */
 | |
|   private showHelp(): void {
 | |
|     console.log(`
 | |
| ${commitinfo.name} v${commitinfo.version}
 | |
| ${commitinfo.description}
 | |
| 
 | |
| Usage: mailer <command> [options]
 | |
| 
 | |
| Commands:
 | |
|   service <action>              Daemon service control
 | |
|     start                       Start the mailer daemon
 | |
|     stop                        Stop the mailer daemon
 | |
|     restart                     Restart the mailer daemon
 | |
|     status                      Show daemon status
 | |
|     enable                      Enable systemd service
 | |
|     disable                     Disable systemd service
 | |
| 
 | |
|   domain <action> [domain]      Domain management
 | |
|     add <domain>                Add a domain
 | |
|     remove <domain>             Remove a domain
 | |
|     list                        List all domains
 | |
| 
 | |
|   dns <action> <domain>         DNS management
 | |
|     setup <domain>              Auto-configure DNS via Cloudflare
 | |
|     validate <domain>           Validate DNS configuration
 | |
|     show <domain>               Show required DNS records
 | |
| 
 | |
|   send [options]                Send an email
 | |
|     --from <email>              Sender email address
 | |
|     --to <email>                Recipient email address
 | |
|     --subject <subject>         Email subject
 | |
|     --text <text>               Email body text
 | |
| 
 | |
|   config <action>               Configuration management
 | |
|     show                        Show current configuration
 | |
|     set <key> <value>           Set configuration value
 | |
| 
 | |
|   version, -v, --version        Show version information
 | |
|   help, -h, --help              Show this help message
 | |
| 
 | |
| Examples:
 | |
|   mailer service start          Start the mailer daemon
 | |
|   mailer domain add example.com Add example.com domain
 | |
|   mailer dns setup example.com  Setup DNS for example.com
 | |
|   mailer send --from sender@example.com --to recipient@example.com \\
 | |
|               --subject "Hello" --text "World"
 | |
| 
 | |
| For more information, visit:
 | |
|   https://code.foss.global/serve.zone/mailer
 | |
| `);
 | |
|   }
 | |
| }
 |