parent
e86694c57b
commit
7e9647b1ae
|
@ -12,6 +12,8 @@
|
|||
- [qBittorrent](https://github.com/qbittorrent/qBittorrent)
|
||||
- [Syncthing](https://github.com/syncthing/syncthing)
|
||||
- [National Weather Service](https://www.weather.gov/)
|
||||
- [Amcrest Cameras](https://amcrest.com/)
|
||||
- [Infisical](https://infisical.com/)
|
||||
|
||||
## NATS Topics
|
||||
|
||||
|
|
|
@ -34,15 +34,15 @@ const (
|
|||
HA_STATE_PREFIX = "homeassistant.states"
|
||||
)
|
||||
|
||||
func Listen(parentLogger *slog.Logger, readme []byte) {
|
||||
func Listen(parentLogger *slog.Logger, parentConfig *config.HatsConfig, readme []byte) {
|
||||
logger = parentLogger
|
||||
cfg = parentConfig
|
||||
|
||||
// render readme to HTML
|
||||
p := parser.NewWithExtensions(parser.CommonExtensions | parser.AutoHeadingIDs | parser.NoEmptyLineBeforeBlock)
|
||||
doc := p.Parse(readme)
|
||||
homepage = string(markdown.Render(doc, html.NewRenderer(html.RendererOptions{Flags: html.CommonFlags | html.HrefTargetBlank})))
|
||||
|
||||
cfg = config.FromEnvironment()
|
||||
haClient = homeassistant.NewRestClient(cfg.GetHomeAssistantBaseUrl(), cfg.HomeAssistantToken)
|
||||
router := chi.NewRouter()
|
||||
|
||||
|
|
|
@ -41,9 +41,9 @@ func CloseSubscription() error {
|
|||
return nil
|
||||
}
|
||||
|
||||
func Subscribe(parentLogger *slog.Logger) error {
|
||||
func Subscribe(parentLogger *slog.Logger, parentConfig *config.HatsConfig) error {
|
||||
logger = parentLogger
|
||||
cfg = config.FromEnvironment()
|
||||
cfg = parentConfig
|
||||
var err error
|
||||
|
||||
url := cfg.GetHomeAssistantWebsocketUrl()
|
||||
|
@ -72,7 +72,7 @@ func reconnect() {
|
|||
time.Sleep(time.Duration(attempts) * 5 * time.Second)
|
||||
logger.Info("Trying to reconnect to Home Assistant", "attempt", attempts)
|
||||
|
||||
err := Subscribe(logger)
|
||||
err := Subscribe(logger, cfg)
|
||||
if err == nil {
|
||||
break
|
||||
}
|
||||
|
|
|
@ -25,10 +25,10 @@ func Close() {
|
|||
client.Close()
|
||||
}
|
||||
|
||||
func JetstreamConnect(parentContext context.Context, parentLogger *slog.Logger) error {
|
||||
func JetstreamConnect(parentContext context.Context, parentLogger *slog.Logger, parentConfig *config.HatsConfig) error {
|
||||
ctx = parentContext
|
||||
logger = parentLogger
|
||||
cfg = config.FromEnvironment()
|
||||
cfg = parentConfig
|
||||
var err error
|
||||
|
||||
client = n.DefaultNatsConnection().WithHostName(cfg.NatsHost).WithPort(cfg.NatsPort).WithConnectionOption(nats.Name(cfg.NatsClientName))
|
||||
|
|
|
@ -5,7 +5,6 @@ import (
|
|||
"strings"
|
||||
"time"
|
||||
|
||||
"code.jhot.me/jhot/hats/pkg/config"
|
||||
"code.jhot.me/jhot/hats/pkg/homeassistant"
|
||||
)
|
||||
|
||||
|
@ -15,7 +14,6 @@ var (
|
|||
|
||||
func initHomeAssistantClient() {
|
||||
if haClient == nil {
|
||||
cfg = config.FromEnvironment()
|
||||
haClient = homeassistant.NewRestClient(cfg.GetHomeAssistantBaseUrl(), cfg.HomeAssistantToken)
|
||||
}
|
||||
}
|
||||
|
|
14
main.go
14
main.go
|
@ -26,15 +26,21 @@ var (
|
|||
)
|
||||
|
||||
func main() {
|
||||
cfg = config.FromEnvironment()
|
||||
var err error
|
||||
cfg, err = config.New()
|
||||
ctx, cancel = context.WithCancel(context.Background())
|
||||
logger = slog.New(slog.NewJSONHandler(os.Stdout, &slog.HandlerOptions{
|
||||
Level: cfg.GetLogLevel(),
|
||||
}))
|
||||
|
||||
if err != nil {
|
||||
logger.Error("Error during config initialization", "error", err)
|
||||
}
|
||||
|
||||
interrupt = make(chan os.Signal, 1)
|
||||
signal.Notify(interrupt, os.Interrupt)
|
||||
|
||||
err := nats.JetstreamConnect(ctx, logger)
|
||||
err = nats.JetstreamConnect(ctx, logger, cfg)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
@ -52,7 +58,7 @@ func main() {
|
|||
nats.GetExistingSchedules()
|
||||
defer nats.StopSchedules()
|
||||
|
||||
err = homeassistant.Subscribe(logger)
|
||||
err = homeassistant.Subscribe(logger, cfg)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
@ -60,7 +66,7 @@ func main() {
|
|||
|
||||
ntfy.InitClient(cfg)
|
||||
|
||||
api.Listen(logger, readme)
|
||||
api.Listen(logger, cfg, readme)
|
||||
defer api.Close()
|
||||
|
||||
for sig := range interrupt {
|
||||
|
|
|
@ -3,71 +3,168 @@ package config
|
|||
import (
|
||||
"fmt"
|
||||
"log/slog"
|
||||
"os"
|
||||
"reflect"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"code.jhot.me/jhot/hats/internal/util"
|
||||
"code.jhot.me/jhot/hats/pkg/infisical"
|
||||
)
|
||||
|
||||
type HatsConfig struct {
|
||||
LogLevl string
|
||||
LogLevl string `config:"LOG_LEVEL" default:"INFO"`
|
||||
|
||||
HomeAssistantHost string
|
||||
HomeAssistantPort string
|
||||
HomeAssistantSecure bool
|
||||
HomeAssistantToken string
|
||||
InfisicalHost string `config:"INFISICAL_HOST" default:"http://infisical:8080"`
|
||||
InfisicalClientID string `config:"INFISICAL_CLIENT" default:""`
|
||||
InfisicalClientSecret string `config:"INFISICAL_SECRET" default:""`
|
||||
InfisicalProjectID string `config:"INFISICAL_PROJECT" default:""`
|
||||
InfisicalEnvironment string `config:"INFISICAL_ENVIRONMENT" default:"prod"`
|
||||
|
||||
NatsHost string
|
||||
NatsPort string
|
||||
NatsToken string
|
||||
NatsClientName string
|
||||
HomeAssistantHost string `config:"HASS_HOST" default:"127.0.0.1"`
|
||||
HomeAssistantPort string `config:"HASS_PORT" default:"8123"`
|
||||
HomeAssistantSecure bool `config:"HASS_SECURE" default:"false"`
|
||||
HomeAssistantToken string `config:"HASS_TOKEN" default:""`
|
||||
|
||||
HatsHost string
|
||||
HatsPort string
|
||||
HatsToken string
|
||||
HatsSecure bool
|
||||
NatsHost string `config:"NATS_HOST" default:"127.0.0.1"`
|
||||
NatsPort string `config:"NATS_PORT" default:"4222"`
|
||||
NatsToken string `config:"NATS_TOKEN" default:""`
|
||||
NatsClientName string `config:"NATS_CLIENT_NAME" default:"hats"`
|
||||
|
||||
NtfyHost string
|
||||
NtfyToken string
|
||||
HatsHost string `config:"HATS_HOST" default:"hats"`
|
||||
HatsPort string `config:"HATS_PORT" default:"8888"`
|
||||
HatsToken string `config:"HATS_TOKEN" default:""`
|
||||
HatsSecure bool `config:"HATS_SECURE" default:"false"`
|
||||
|
||||
ConfigDir string
|
||||
NtfyHost string `config:"NTFY_HOST" default:"https://ntfy.sh"`
|
||||
NtfyToken string `config:"NTFY_TOKEN" default:""`
|
||||
|
||||
SyncthingHost string `config:"SYNCTHING_HOST" default:"http://127.0.0.1:8384"`
|
||||
SyncthingToken string `config:"SYNCTHING_TOKEN" default:""`
|
||||
|
||||
GokapiHost string `config:"GOKAPI_HOST" default:"http://gokapi:53842"`
|
||||
GokapiToken string `config:"GOKAPI_TOKEN" default:""`
|
||||
|
||||
QbittorrentHost string `config:"QBITTORRENT_HOST" default:"http://qbittorrent:8080"`
|
||||
QbittorrentUser string `config:"QBITTORRENT_USER" default:""`
|
||||
QbittorrentPassword string `config:"QBITTORRENT_PASS" default:""`
|
||||
|
||||
ConfigDir string `config:"CONFIG_DIR" default:"/config"`
|
||||
|
||||
infisicalClient *infisical.InfisicalClient
|
||||
infisicalRetrievalOpts *infisical.RetrieveSecretOptions
|
||||
}
|
||||
|
||||
func FromEnvironment() *HatsConfig {
|
||||
config := &HatsConfig{
|
||||
LogLevl: util.GetEnvWithDefault("LOG_LEVEL", "INFO"),
|
||||
func New() (*HatsConfig, error) {
|
||||
cfg := &HatsConfig{}
|
||||
cfg.SetValues(ConfigValueSourceDefault).SetValues(ConfigValueSourceEnv)
|
||||
|
||||
HomeAssistantHost: util.GetEnvWithDefault("HASS_HOST", "127.0.0.1"),
|
||||
HomeAssistantPort: util.GetEnvWithDefault("HASS_PORT", "8123"),
|
||||
HomeAssistantToken: util.GetEnvWithDefault("HASS_TOKEN", ""),
|
||||
if cfg.InfisicalConfigured() {
|
||||
cfg.infisicalClient = infisical.New(cfg.InfisicalHost, cfg.InfisicalClientID, cfg.InfisicalClientSecret)
|
||||
|
||||
NatsHost: util.GetEnvWithDefault("NATS_HOST", "127.0.0.1"),
|
||||
NatsPort: util.GetEnvWithDefault("NATS_PORT", "4222"),
|
||||
NatsToken: util.GetEnvWithDefault("NATS_TOKEN", ""),
|
||||
NatsClientName: util.GetEnvWithDefault("NATS_CLIENT_NAME", "hats"),
|
||||
|
||||
HatsHost: util.GetEnvWithDefault("HATS_HOST", "hats"),
|
||||
HatsPort: util.GetEnvWithDefault("HATS_PORT", "8888"),
|
||||
HatsToken: util.GetEnvWithDefault("HATS_TOKEN", ""),
|
||||
|
||||
NtfyHost: util.GetEnvWithDefault("NTFY_HOST", "https://ntfy.sh"),
|
||||
NtfyToken: util.GetEnvWithDefault("NTFY_TOKEN", ""),
|
||||
|
||||
ConfigDir: util.GetEnvWithDefault("CONFIG_DIR", "/config"),
|
||||
err := cfg.infisicalClient.Login()
|
||||
if err != nil {
|
||||
cfg.infisicalClient = nil
|
||||
return cfg, fmt.Errorf("error logging in to Infisical: %w", err)
|
||||
}
|
||||
|
||||
config.HomeAssistantSecure, _ = strconv.ParseBool(util.GetEnvWithDefault("HASS_SECURE", "false"))
|
||||
config.HatsSecure, _ = strconv.ParseBool(util.GetEnvWithDefault("HATS_SECURE", "false"))
|
||||
cfg.infisicalRetrievalOpts = &infisical.RetrieveSecretOptions{
|
||||
WorkspaceID: cfg.InfisicalProjectID,
|
||||
Environment: cfg.InfisicalEnvironment,
|
||||
SecretPath: "/",
|
||||
IncludeImports: false,
|
||||
}
|
||||
|
||||
return config
|
||||
secrets, err := cfg.infisicalClient.ListSecrets(cfg.infisicalRetrievalOpts)
|
||||
if err != nil {
|
||||
return cfg, fmt.Errorf("error getting Infisical secrets: %w", err)
|
||||
}
|
||||
|
||||
secretsMap := make(map[string]string)
|
||||
for _, secret := range secrets {
|
||||
secretsMap[secret.SecretKey] = secret.SecretValue
|
||||
}
|
||||
|
||||
cfg.SetValues(ConfigValueSourceInfisical, secretsMap)
|
||||
}
|
||||
|
||||
return cfg, nil
|
||||
}
|
||||
|
||||
type ConfigValueSource string
|
||||
|
||||
const ConfigValueSourceEnv ConfigValueSource = "env"
|
||||
const ConfigValueSourceDefault ConfigValueSource = "default"
|
||||
const ConfigValueSourceInfisical ConfigValueSource = "infisical"
|
||||
|
||||
func (c *HatsConfig) SetValues(source ConfigValueSource, inputs ...map[string]string) *HatsConfig {
|
||||
fields := reflect.VisibleFields(reflect.TypeOf(*c))
|
||||
|
||||
for _, field := range fields {
|
||||
envName := field.Tag.Get("config")
|
||||
if envName == "" {
|
||||
continue
|
||||
}
|
||||
|
||||
var envValue string
|
||||
switch source {
|
||||
case ConfigValueSourceDefault:
|
||||
envValue = field.Tag.Get("default")
|
||||
case ConfigValueSourceEnv:
|
||||
envValue = os.Getenv(envName)
|
||||
case ConfigValueSourceInfisical:
|
||||
if len(inputs) == 0 {
|
||||
break
|
||||
}
|
||||
if val, found := inputs[0][envName]; found {
|
||||
envValue = val
|
||||
}
|
||||
}
|
||||
|
||||
if envValue == "" {
|
||||
continue
|
||||
}
|
||||
|
||||
f := reflect.ValueOf(c).Elem().FieldByName(field.Name)
|
||||
switch field.Type.Kind() {
|
||||
case reflect.Bool:
|
||||
f.SetBool(strings.EqualFold(envValue, "true"))
|
||||
case reflect.Int:
|
||||
parsed, err := strconv.ParseInt(envValue, 10, 64)
|
||||
if err == nil {
|
||||
f.SetInt(parsed)
|
||||
}
|
||||
case reflect.String:
|
||||
f.SetString(envValue)
|
||||
}
|
||||
}
|
||||
|
||||
return c
|
||||
}
|
||||
|
||||
func (c *HatsConfig) GetCustomSetting(name string, defaultValue string) string {
|
||||
returnValue := defaultValue
|
||||
|
||||
envValue := os.Getenv(name)
|
||||
if envValue != "" {
|
||||
returnValue = envValue
|
||||
}
|
||||
|
||||
if c.InfisicalConfigured() {
|
||||
secret, _ := c.infisicalClient.GetSecret(name, c.infisicalRetrievalOpts)
|
||||
if secret.SecretValue != "" {
|
||||
returnValue = secret.SecretValue
|
||||
}
|
||||
}
|
||||
|
||||
return returnValue
|
||||
}
|
||||
|
||||
func (c *HatsConfig) GetHomeAssistantBaseUrl() string {
|
||||
hassProtocol := "http"
|
||||
protocol := "http"
|
||||
if c.HomeAssistantSecure {
|
||||
hassProtocol += "s"
|
||||
protocol += "s"
|
||||
}
|
||||
return fmt.Sprintf("%s://%s:%s", hassProtocol, c.HomeAssistantHost, c.HomeAssistantPort)
|
||||
return fmt.Sprintf("%s://%s:%s", protocol, c.HomeAssistantHost, c.HomeAssistantPort)
|
||||
}
|
||||
|
||||
func (c *HatsConfig) GetHomeAssistantWebsocketUrl() string {
|
||||
|
@ -102,3 +199,11 @@ func (c *HatsConfig) GetLogLevel() slog.Level {
|
|||
return slog.LevelInfo
|
||||
}
|
||||
}
|
||||
|
||||
func (c *HatsConfig) InfisicalConfigured() bool {
|
||||
return c.InfisicalHost != "" &&
|
||||
c.InfisicalClientID != "" &&
|
||||
c.InfisicalClientSecret != "" &&
|
||||
c.InfisicalProjectID != "" &&
|
||||
c.InfisicalEnvironment != ""
|
||||
}
|
||||
|
|
|
@ -27,10 +27,6 @@ func New(host string, token string) *GokapiClient {
|
|||
}
|
||||
}
|
||||
|
||||
func NewFromEnv() *GokapiClient {
|
||||
return New(os.Getenv("GOKAPI_HOST"), os.Getenv("GOKAPI_TOKEN"))
|
||||
}
|
||||
|
||||
type GokapiFile struct {
|
||||
ID string `json:"Id"`
|
||||
Name string `json:"Name"`
|
||||
|
|
|
@ -0,0 +1,114 @@
|
|||
package infisical
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"code.jhot.me/jhot/hats/internal/util"
|
||||
"github.com/go-resty/resty/v2"
|
||||
)
|
||||
|
||||
type InfisicalClient struct {
|
||||
restClient *resty.Client
|
||||
host string
|
||||
clientId string
|
||||
clientSecret string
|
||||
accessToken string
|
||||
tokenExpiration time.Time
|
||||
}
|
||||
|
||||
func New(host string, clientId string, clientSecret string) *InfisicalClient {
|
||||
return &InfisicalClient{
|
||||
restClient: resty.New().SetBaseURL(fmt.Sprintf("%s/api", host)),
|
||||
host: host,
|
||||
clientId: clientId,
|
||||
clientSecret: clientSecret,
|
||||
accessToken: "",
|
||||
tokenExpiration: time.Now(),
|
||||
}
|
||||
}
|
||||
|
||||
func (c *InfisicalClient) Login() error {
|
||||
var result LoginResponse
|
||||
_, err := util.CheckSuccess(c.restClient.R().SetFormData(map[string]string{
|
||||
"clientId": c.clientId,
|
||||
"clientSecret": c.clientSecret,
|
||||
}).SetResult(&result).Post("v1/auth/universal-auth/login"))
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
c.restClient.SetAuthScheme(result.TokenType).SetAuthToken(result.AccessToken)
|
||||
c.accessToken = result.AccessToken
|
||||
c.tokenExpiration = time.Now().Add(time.Duration(result.ExpiresIn) * time.Second)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *InfisicalClient) CheckToken() error {
|
||||
if c.accessToken == "" {
|
||||
return fmt.Errorf("access token empty, please login first")
|
||||
}
|
||||
|
||||
if time.Now().Before(c.tokenExpiration) {
|
||||
return nil
|
||||
}
|
||||
|
||||
var result LoginResponse
|
||||
_, err := util.CheckSuccess(c.restClient.R().SetResult(&result).SetBody(map[string]interface{}{
|
||||
"accessToken": c.accessToken,
|
||||
}).Post("v1/auth/token/renew"))
|
||||
|
||||
if err != nil {
|
||||
return fmt.Errorf("error renewing token: %w", err)
|
||||
}
|
||||
|
||||
c.restClient.SetAuthScheme(result.TokenType).SetAuthToken(result.AccessToken)
|
||||
c.accessToken = result.AccessToken
|
||||
c.tokenExpiration = time.Now().Add(time.Duration(result.ExpiresIn) * time.Second)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *InfisicalClient) ListSecrets(opts *RetrieveSecretOptions) ([]Secret, error) {
|
||||
err := c.CheckToken()
|
||||
if err != nil {
|
||||
return []Secret{}, err
|
||||
}
|
||||
|
||||
var result SecretsResponse
|
||||
_, err = util.CheckSuccess(c.restClient.R().SetResult(&result).SetQueryParams(map[string]string{
|
||||
"workspaceId": opts.WorkspaceID,
|
||||
"environment": opts.Environment,
|
||||
"secretPath": opts.SecretPath,
|
||||
"include_imports": fmt.Sprintf("%t", opts.IncludeImports),
|
||||
}).Get("v3/secrets/raw"))
|
||||
|
||||
if err != nil {
|
||||
return []Secret{}, err
|
||||
}
|
||||
|
||||
return result.Secrets, nil
|
||||
}
|
||||
|
||||
func (c *InfisicalClient) GetSecret(name string, opts *RetrieveSecretOptions) (Secret, error) {
|
||||
err := c.CheckToken()
|
||||
if err != nil {
|
||||
return Secret{}, err
|
||||
}
|
||||
|
||||
var result SecretResponse
|
||||
_, err = util.CheckSuccess(c.restClient.R().SetResult(&result).SetQueryParams(map[string]string{
|
||||
"workspaceId": opts.WorkspaceID,
|
||||
"environment": opts.Environment,
|
||||
"secretPath": opts.SecretPath,
|
||||
"include_imports": fmt.Sprintf("%t", opts.IncludeImports),
|
||||
"type": "shared",
|
||||
}).Get("v3/secrets/raw/" + name))
|
||||
|
||||
if err != nil {
|
||||
return Secret{}, err
|
||||
}
|
||||
|
||||
return result.Secret, nil
|
||||
}
|
|
@ -0,0 +1,62 @@
|
|||
package infisical
|
||||
|
||||
type LoginResponse struct {
|
||||
AccessToken string `json:"accessToken"`
|
||||
ExpiresIn int `json:"expiresIn"`
|
||||
AccessTokenMaxTTL int `json:"accessTokenMaxTTL"`
|
||||
TokenType string `json:"tokenType"`
|
||||
}
|
||||
|
||||
type RetrieveSecretOptions struct {
|
||||
WorkspaceID string
|
||||
Environment string
|
||||
SecretPath string
|
||||
IncludeImports bool
|
||||
}
|
||||
|
||||
func DefaultRetrieveSecretOptions() *RetrieveSecretOptions {
|
||||
return &RetrieveSecretOptions{
|
||||
WorkspaceID: "",
|
||||
Environment: "",
|
||||
SecretPath: "/",
|
||||
IncludeImports: false,
|
||||
}
|
||||
}
|
||||
|
||||
func (o *RetrieveSecretOptions) WithWorkspaceID(workspaceId string) *RetrieveSecretOptions {
|
||||
o.WorkspaceID = workspaceId
|
||||
return o
|
||||
}
|
||||
|
||||
func (o *RetrieveSecretOptions) WithEnvironment(environment string) *RetrieveSecretOptions {
|
||||
o.Environment = environment
|
||||
return o
|
||||
}
|
||||
|
||||
func (o *RetrieveSecretOptions) WithSecretPath(secretPath string) *RetrieveSecretOptions {
|
||||
o.SecretPath = secretPath
|
||||
return o
|
||||
}
|
||||
|
||||
func (o *RetrieveSecretOptions) WithImports(includeImports bool) *RetrieveSecretOptions {
|
||||
o.IncludeImports = includeImports
|
||||
return o
|
||||
}
|
||||
|
||||
type Secret struct {
|
||||
ID string `json:"_id"`
|
||||
Environment string `json:"environment,omitempty"`
|
||||
SecretComment string `json:"secretComment,omitempty"`
|
||||
SecretKey string `json:"secretKey,omitempty"`
|
||||
SecretValue string `json:"secretValue,omitempty"`
|
||||
Version int `json:"version,omitempty"`
|
||||
Workspace string `json:"workspace,omitempty"`
|
||||
}
|
||||
|
||||
type SecretsResponse struct {
|
||||
Secrets []Secret `json:"secrets"`
|
||||
}
|
||||
|
||||
type SecretResponse struct {
|
||||
Secret Secret `json:"secret"`
|
||||
}
|
|
@ -3,7 +3,6 @@ package qbittorrent
|
|||
import (
|
||||
"fmt"
|
||||
"net/http"
|
||||
"os"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
|
@ -21,12 +20,6 @@ func New(host string) *QbittorrentClient {
|
|||
}
|
||||
}
|
||||
|
||||
func NewFromEnv() (*QbittorrentClient, error) {
|
||||
c := New(os.Getenv("QBITTORRENT_HOST"))
|
||||
err := c.Login(os.Getenv("QBITTORRENT_USER"), os.Getenv("QBITTORRENT_PASS"))
|
||||
return c, err
|
||||
}
|
||||
|
||||
func (c *QbittorrentClient) Login(user string, pass string) error {
|
||||
resp, err := util.CheckSuccess(c.restClient.R().SetFormData(map[string]string{
|
||||
"username": user,
|
||||
|
|
|
@ -2,7 +2,6 @@ package syncthing
|
|||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
|
||||
"code.jhot.me/jhot/hats/internal/util"
|
||||
"github.com/go-resty/resty/v2"
|
||||
|
@ -20,10 +19,6 @@ func New(host string, token string) *SyncthingClient {
|
|||
}
|
||||
}
|
||||
|
||||
func NewFromEnv() *SyncthingClient {
|
||||
return New(os.Getenv("SYNCTHING_HOST"), os.Getenv("SYNCTHING_TOKEN"))
|
||||
}
|
||||
|
||||
func (c *SyncthingClient) GetVersion() (VersionInfo, error) {
|
||||
var data VersionInfo
|
||||
_, err := util.CheckSuccess(c.restClient.R().SetResult(&data).Get("system/version"))
|
||||
|
|
Loading…
Reference in New Issue