runWizard is a text-driven first-run flow. It guides the user through five steps, runs the scanner, writes a complete config.yaml, and optionally patches an existing proxy client's config.
(out io.Writer, in *bufio.Reader, cfgPath string)
| 26 | // five steps, runs the scanner, writes a complete config.yaml, and |
| 27 | // optionally patches an existing proxy client's config. |
| 28 | func runWizard(out io.Writer, in *bufio.Reader, cfgPath string) error { |
| 29 | fmt.Fprintln(out) |
| 30 | fmt.Fprintln(out, "snix first-run wizard") |
| 31 | fmt.Fprintln(out, "─────────────────────") |
| 32 | fmt.Fprintln(out) |
| 33 | fmt.Fprintln(out, "Five short steps. Safe to Ctrl-C at any time; nothing is written") |
| 34 | fmt.Fprintln(out, "to disk until the end.") |
| 35 | fmt.Fprintln(out) |
| 36 | |
| 37 | // Step 1 — do you have a proxy server? |
| 38 | fmt.Fprintln(out, "Step 1/5 — Do you already have a proxy server (Xray / VLESS / Trojan / etc.)?") |
| 39 | hasServer := promptYesNo(out, in, "Have a proxy server?", true) |
| 40 | |
| 41 | var connectHost string |
| 42 | var connectPort uint16 = 443 |
| 43 | |
| 44 | if hasServer { |
| 45 | // Step 2a — paste server details. |
| 46 | fmt.Fprintln(out, "\nStep 2/5 — Enter your proxy server.") |
| 47 | connectHost = promptLine(out, in, " Host (domain or IP): ", "") |
| 48 | for connectHost == "" { |
| 49 | connectHost = promptLine(out, in, " Host cannot be empty. Try again: ", "") |
| 50 | } |
| 51 | portStr := promptLine(out, in, " Port [443]: ", "443") |
| 52 | if n, err := strconv.Atoi(portStr); err == nil && n > 0 && n < 65536 { |
| 53 | connectPort = uint16(n) |
| 54 | } |
| 55 | } else { |
| 56 | // Step 2b — offer the Cloudflare Worker flow. |
| 57 | fmt.Fprintln(out) |
| 58 | fmt.Fprintln(out, " You don't have a proxy server yet. snix can help you deploy a") |
| 59 | fmt.Fprintln(out, " free Cloudflare Worker that acts as your proxy in ~60 seconds.") |
| 60 | fmt.Fprintln(out, " (Free tier covers a lot of normal browsing.)") |
| 61 | fmt.Fprintln(out) |
| 62 | fmt.Fprintln(out, " What you'll need:") |
| 63 | fmt.Fprintln(out, " • A free Cloudflare account (https://dash.cloudflare.com/sign-up)") |
| 64 | fmt.Fprintln(out, " • A browser to click through the deploy wizard") |
| 65 | fmt.Fprintln(out) |
| 66 | |
| 67 | if promptYesNo(out, in, "Deploy a Cloudflare Worker now?", true) { |
| 68 | uuid := randomUUID() |
| 69 | fmt.Fprintln(out) |
| 70 | fmt.Fprintln(out, " 1. Open this URL in your browser:") |
| 71 | fmt.Fprintln(out) |
| 72 | fmt.Fprintln(out, " https://dash.cloudflare.com/?to=/:account/workers/services/new") |
| 73 | fmt.Fprintln(out) |
| 74 | fmt.Fprintln(out, " 2. Create a new Worker called 'snix' (any name works).") |
| 75 | fmt.Fprintln(out, " 3. Paste the contents of cfworker/worker.js from this repo into the editor.") |
| 76 | fmt.Fprintln(out, " 4. Under Variables, add UUID =", uuid) |
| 77 | fmt.Fprintln(out, " 5. Deploy. Copy the 'workers.dev' URL shown at the top.") |
| 78 | fmt.Fprintln(out) |
| 79 | workerHost := promptLine(out, in, " Paste the worker host (e.g. snix.yourname.workers.dev): ", "") |
| 80 | for workerHost == "" { |
| 81 | workerHost = promptLine(out, in, " Required. Try again: ", "") |
| 82 | } |
| 83 | workerHost = strings.TrimPrefix(workerHost, "https://") |
| 84 | workerHost = strings.TrimPrefix(workerHost, "http://") |
| 85 | workerHost = strings.TrimSuffix(workerHost, "/") |
no test coverage detected