porta-rs 0.1.1

Zero-trust CGNAT bypass via WireGuard tunnel
use anyhow::Result;
use clap::Args;

use crate::config::{load_config, PortMapping, Protocol, Role};
use crate::ufw::rules::UfwManager;

#[derive(Args)]
pub struct AddPortArgs {
    /// Remote port to listen on (public side)
    #[arg(short, long)]
    pub remote: u16,

    /// Local port to forward to
    #[arg(short, long)]
    pub local: u16,

    /// Protocol: tcp or udp
    #[arg(short, long, default_value = "tcp")]
    pub protocol: Protocol,

    /// Description for this port mapping
    #[arg(short, long, default_value = "")]
    pub desc: String,

    /// Skip UFW rule creation
    #[arg(long)]
    pub no_ufw: bool,
}

pub fn execute(args: &AddPortArgs) -> Result<()> {
    let mut config = load_config()?;

    // Check if remote port is already in use
    if config.ports().iter().any(|p| p.remote_port == args.remote && p.protocol == args.protocol) {
        return Err(anyhow::anyhow!(
            "Remote port {} ({}) is already in use",
            args.remote,
            args.protocol
        ));
    }

    let id = format!("port-{}", args.remote);
    let mapping = PortMapping {
        id: id.clone(),
        local_port: args.local,
        remote_port: args.remote,
        protocol: args.protocol.clone(),
        description: args.desc.clone(),
        enabled: true,
    };

    config.ports_mut().push(mapping);
    config.save()?;

    // Add UFW rule if on server
    if config.role() == Role::Server && !args.no_ufw {
        let ufw = UfwManager::new();
        if ufw.is_active() {
            match ufw.allow_port(args.remote, &args.protocol) {
                Ok(()) => {
                    println!("\x1b[1;32m✓ UFW rule added: allow {} {}\x1b[0m", args.remote, args.protocol);
                }
                Err(e) => {
                    println!("\x1b[1;33m⚠ Failed to add UFW rule: {}\x1b[0m", e);
                }
            }
        } else {
            println!("\x1b[1;33m⚠ UFW is not active, skipping firewall rule\x1b[0m");
        }
    }

    println!("\n\x1b[1;32m✓ Port forwarding added:\x1b[0m");
    println!("  Remote:  :{} ({})", args.remote, args.protocol);
    println!("  Local:   localhost:{}", args.local);
    println!("  ID:      {}", id);
    if !args.desc.is_empty() {
        println!("  Desc:    {}", args.desc);
    }
    println!();

    Ok(())
}