package gluetun import ( "encoding/json" "errors" "fmt" "io" "net/http" "os" "strings" "git.aetherial.dev/aeth/gluetun-qbitt-sidecar/pkg/shared" "github.com/pelletier/go-toml/v2" ) /* will need to parse a configuration like this: [[roles]] name = "qbittorrent" # Define a list of routes with the syntax "Http-Method /path" routes = ["GET /v1/portforward"] # Define an authentication method with its parameters auth = "basic" username = "myusername" password = "mypassword" Or if using the API key authentication method: auth = "apikey" apikey = "myapikey" the 'apikey' is then sent in the X-API-Key header we will need to extract the username and password for authentication to gluetun. This will be done by mounting the same config file that gluetun has to a static location, like /gluetun-qbitt-sidecar/config.toml will need to just retrieve the exposed port via the route: GET /v1/portforward ----> {"port":5914} */ const ( PORT_FORWARD_PATH = "v1/portforward" STATUS_PATH = "v1/vpn/status" API_KEY_HEADER = "X-API-Key" GLUETUN_ADDRESS = "GLUETUN_ADDRESS" ) type GluetunClient struct { client *http.Client server shared.Server } type Port struct { Port int `json:"port"` } type Configuration struct { Roles []Role `toml:"roles"` } type Role struct { Name string `toml:"name"` Routes []string `toml:"routes"` Auth string `toml:"auth"` Username string `toml:"username,omitempty"` Password string `toml:"password,omitempty"` Apikey string `toml:"apikey,omitempty"` } func NewGluetunClient() {} /* Read in the config.toml file that gluetun uses to set the api key / username and password :param path: the path to the config file. Use the path for where this is running, since this can be either ran as a container next to your deployment, or on the host itself */ func GetAuthentication(path string) (Role, error) { var config Configuration var role Role fb, err := os.ReadFile(path) if err != nil { return role, err } fmt.Printf("%s\n", string(fb)) err = toml.Unmarshal(fb, &config) if err != nil { return role, err } for i := range config.Roles { for x := range config.Roles[i].Routes { if strings.Contains(config.Roles[i].Routes[x], PORT_FORWARD_PATH) { return config.Roles[i], nil } } } return role, errors.New(fmt.Sprintf("No routes found that contained the path: '%s'", PORT_FORWARD_PATH)) } // get the gluetun address func GetGluetunAddress() (shared.Server, error) { var srv shared.Server client := http.Client{} srv, err := shared.ParseServer("http://gluetun:8000") if err != nil { return srv, err } req, err := http.NewRequest(http.MethodGet, srv.FormatWith(STATUS_PATH), nil) if err != nil { return srv, err } resp, err := client.Do(req) if err != nil { return srv, err } if resp.StatusCode == http.StatusUnauthorized { // can assume that unauthorized means that the server is alive on that endpoint return srv, nil } // try to parse out the environment variable that supposed to carry the value (if it is different) addressFromEnv := os.Getenv(GLUETUN_ADDRESS) srv, err = shared.ParseServer(addressFromEnv) if err != nil { return srv, err } return srv, nil } /* Gets the forwarded port from the server */ func GetForwardedPort(address string, apiKey string) (Port, error) { formattedUrl := fmt.Sprintf("http://%s/%s", sanitizeUrl(address), PORT_FORWARD_PATH) req, err := http.NewRequest(http.MethodGet, formattedUrl, nil) if err != nil { return Port{}, err } req.Header.Add(API_KEY_HEADER, apiKey) client := http.Client{} resp, err := client.Do(req) if err != nil { return Port{}, err } var b []byte defer resp.Body.Close() b, err = io.ReadAll(resp.Body) if err != nil { return Port{}, err } var port Port err = json.Unmarshal(b, &port) if err != nil { return port, err } return port, nil }