initial
This commit is contained in:
6
ts/cli/index.ts
Normal file
6
ts/cli/index.ts
Normal file
@@ -0,0 +1,6 @@
|
||||
/**
|
||||
* CLI module
|
||||
* Command-line interface for mailer
|
||||
*/
|
||||
|
||||
export * from './mailer-cli.ts';
|
||||
387
ts/cli/mailer-cli.ts
Normal file
387
ts/cli/mailer-cli.ts
Normal file
@@ -0,0 +1,387 @@
|
||||
/**
|
||||
* 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
|
||||
`);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user