package main import ( "bufio" "flag" "fmt" "os" "strconv" "strings" ) func main() { subdomain := flag.String("subdomain", "", "subdomain name (without .domain)") port := flag.String("port", "", "target port") yes := flag.Bool("y", false, "skip confirmation") flag.Parse() cfg, err := LoadOrCreateConfig() if err != nil { fmt.Fprintf(os.Stderr, "Error: %v\n", err) os.Exit(1) } reader := bufio.NewReader(os.Stdin) if *subdomain == "" { fmt.Printf("Subdomain (without .%s): ", cfg.Domain) *subdomain, _ = reader.ReadString('\n') *subdomain = strings.TrimSpace(*subdomain) } if err := validateSubdomain(*subdomain); err != nil { fmt.Fprintf(os.Stderr, "Error: %v\n", err) os.Exit(1) } if *port == "" { fmt.Print("Target port: ") *port, _ = reader.ReadString('\n') *port = strings.TrimSpace(*port) } if err := validatePort(*port); err != nil { fmt.Fprintf(os.Stderr, "Error: %v\n", err) os.Exit(1) } fqdn := *subdomain + "." + cfg.Domain fmt.Println() fmt.Printf(" DNS: CNAME %s → %s (Porkbun)\n", fqdn, cfg.Domain) fmt.Printf(" Caddy: %s → localhost:%s\n", fqdn, *port) fmt.Println() if !*yes { fmt.Print("Proceed? [Y/n] ") answer, _ := reader.ReadString('\n') answer = strings.TrimSpace(strings.ToLower(answer)) if answer != "" && answer != "y" && answer != "yes" { fmt.Println("Aborted.") os.Exit(0) } } fmt.Println() // Step 1: DNS fmt.Print("[1/2] Creating DNS record on Porkbun... ") _, err = CreateDNSRecord(cfg, *subdomain) if err != nil { fmt.Println("✗") fmt.Fprintf(os.Stderr, "Error: %v\n", err) os.Exit(1) } fmt.Println("✓") // Step 2: Caddy fmt.Print("[2/2] Adding Caddy route... ") err = AddCaddyRoute(cfg, fqdn, *port) if err != nil { fmt.Println("✗") fmt.Fprintf(os.Stderr, "Error: %v\n", err) fmt.Fprintln(os.Stderr, "Warning: DNS record was created successfully. The Caddy route must be added manually.") os.Exit(1) } fmt.Println("✓") fmt.Printf("\nDone! %s → localhost:%s\n", fqdn, *port) } func validateSubdomain(s string) error { if s == "" { return fmt.Errorf("subdomain cannot be empty") } if strings.ContainsAny(s, ". \t") { return fmt.Errorf("subdomain must not contain dots or spaces") } return nil } func validatePort(p string) error { if p == "" { return fmt.Errorf("port cannot be empty") } n, err := strconv.Atoi(p) if err != nil { return fmt.Errorf("port must be a number") } if n < 1 || n > 65535 { return fmt.Errorf("port must be between 1 and 65535") } return nil }