package control import ( "encoding/json" "errors" "fmt" "net/url" "os" "path/filepath" "strings" ) // T5DE's hubs. const ( Production = "https://hub.t5de.dev" Staging = "https://hub.staging.t5de.dev" ) // ChannelDefault is the hub an install uses unless the person chooses one: nightlies and builds outside an // install (channel "nightly", "dev") use staging, releases T5DE's hub. func ChannelDefault(channel string) string { if channel == "nightly" || channel == "dev" { return Staging } return Production } // Server is the hub for this run: env ($T5DE_HUB) if set, else the person's choice (the client's Settings, kept // in choicePath), else the channel's. func Server(choicePath, channel, env string) string { if env != "" { return strings.TrimSuffix(env, "/") } if c := LoadChoice(choicePath); c != "" { return c } return ChannelDefault(channel) } // LoadChoice is the hub the person chose, "" for the channel's. func LoadChoice(path string) string { b, err := os.ReadFile(path) if err != nil { return "" } var c struct { Hub string `json:"hub"` } if json.Unmarshal(b, &c) != nil { return "" } hub, err := CheckHub(c.Hub) if err != nil { return "" } return hub } // SaveChoice remembers the hub the person chose ("" for the channel's). func SaveChoice(path, hub string) error { if hub == "" { if err := os.Remove(path); err != nil && !errors.Is(err, os.ErrNotExist) { return err } return nil } b, _ := json.Marshal(map[string]string{"hub": hub}) if err := os.MkdirAll(filepath.Dir(path), 0o700); err != nil { return err } tmp := path + ".tmp" if err := os.WriteFile(tmp, b, 0o600); err != nil { return err } return os.Rename(tmp, path) } // CheckHub is a hub's address without a trailing slash, if it's https (or this computer's, for development). func CheckHub(s string) (string, error) { s = strings.TrimSuffix(strings.TrimSpace(s), "/") u, err := url.Parse(s) if err != nil || u.Host == "" || u.RawQuery != "" || u.Fragment != "" { return "", fmt.Errorf("not a hub address: %q", s) } local := u.Hostname() == "localhost" || u.Hostname() == "127.0.0.1" if u.Scheme != "https" && !(u.Scheme == "http" && local) { return "", errors.New("the hub needs an https address") } return s, nil } // StateDir is where the proxy keeps what belongs to a hub (its sign-in, product IDs, products): the data folder // itself for T5DE's hub, hubs/ in it for any other, so a hub never gets another's sign-in and product IDs // from two hubs never clash. func StateDir(data, hub string) string { if hub == Production { return data } u, err := url.Parse(hub) name := hub if err == nil { name = u.Host + u.Path } name = strings.Map(func(r rune) rune { if r >= 'a' && r <= 'z' || r >= '0' && r <= '9' || r == '.' || r == '-' { return r } if r >= 'A' && r <= 'Z' { return r + 'a' - 'A' } return '_' }, name) return filepath.Join(data, "hubs", name) }