| 136 | } |
| 137 | |
| 138 | func GenerateConfig(customTunnelHost string) (Config, error) { |
| 139 | priv, err := tunnelsdk.GeneratePrivateKey() |
| 140 | if err != nil { |
| 141 | return Config{}, xerrors.Errorf("generate private key: %w", err) |
| 142 | } |
| 143 | privNoisePublicKey, err := priv.NoisePrivateKey() |
| 144 | if err != nil { |
| 145 | return Config{}, xerrors.Errorf("generate noise private key: %w", err) |
| 146 | } |
| 147 | pubNoisePublicKey := priv.NoisePublicKey() |
| 148 | |
| 149 | spin := spinner.New(spinner.CharSets[39], 350*time.Millisecond) |
| 150 | spin.Suffix = " Finding the closest tunnel region..." |
| 151 | spin.Start() |
| 152 | |
| 153 | nodes, err := Nodes(customTunnelHost) |
| 154 | if err != nil { |
| 155 | return Config{}, xerrors.Errorf("get nodes: %w", err) |
| 156 | } |
| 157 | node, err := FindClosestNode(nodes) |
| 158 | if err != nil { |
| 159 | // If we fail to find the closest node, default to a random node from |
| 160 | // the first region. |
| 161 | region := Regions[0] |
| 162 | n, _ := cryptorand.Intn(len(region.Nodes)) |
| 163 | node = region.Nodes[n] |
| 164 | spin.Stop() |
| 165 | _, _ = fmt.Println("Error picking closest dev tunnel:", err) |
| 166 | _, _ = fmt.Println("Defaulting to", Regions[0].LocationName) |
| 167 | } |
| 168 | |
| 169 | locationName := "Unknown" |
| 170 | if node.RegionID < len(Regions) { |
| 171 | locationName = Regions[node.RegionID].LocationName |
| 172 | } |
| 173 | |
| 174 | spin.Stop() |
| 175 | _, _ = fmt.Printf("Using tunnel in %s with latency %s.\n", |
| 176 | cliui.Keyword(locationName), |
| 177 | cliui.Code(node.AvgLatency.String()), |
| 178 | ) |
| 179 | |
| 180 | return Config{ |
| 181 | Version: tunnelsdk.TunnelVersion2, |
| 182 | PrivateKey: privNoisePublicKey, |
| 183 | PublicKey: pubNoisePublicKey, |
| 184 | Tunnel: node, |
| 185 | }, nil |
| 186 | } |
| 187 | |
| 188 | func writeConfig(cfg Config) error { |
| 189 | cfgFi, err := cfgPath() |