From e17d8bf62ee08a6c0aad4543f8d24715eb3a6298 Mon Sep 17 00:00:00 2001 From: brngates98 Date: Sun, 25 Jan 2026 08:59:11 -0500 Subject: [PATCH] move remote.go to use unifi library functions --- go.mod | 4 +- go.sum | 6 +- pkg/inputunifi/remote.go | 221 ++------------------------------------- 3 files changed, 15 insertions(+), 216 deletions(-) diff --git a/go.mod b/go.mod index a0bd60a3..95fbbc6c 100644 --- a/go.mod +++ b/go.mod @@ -23,7 +23,7 @@ require ( require ( go.yaml.in/yaml/v2 v2.4.3 // indirect - golang.org/x/net v0.48.0 // indirect + golang.org/x/net v0.49.0 // indirect ) require ( @@ -48,4 +48,4 @@ require ( ) // for local iterative development only -// replace github.com/unpoller/unifi/v5 => ../unifi +replace github.com/unpoller/unifi/v5 => ../unifi diff --git a/go.sum b/go.sum index f376d23e..046c16a2 100644 --- a/go.sum +++ b/go.sum @@ -77,8 +77,6 @@ github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U= github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U= -github.com/unpoller/unifi/v5 v5.5.0 h1:MxSbCYBxgg1NN4R0w6h+ES2R4QNM1vhmwjQhJVwVDp0= -github.com/unpoller/unifi/v5 v5.5.0/go.mod h1:pa6zv4Oyb1nFEm4qu/8CUv8Q25hQof04Wh2D0RXcTYc= github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto= go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE= @@ -92,8 +90,8 @@ golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM= -golang.org/x/net v0.48.0 h1:zyQRTTrjc33Lhh0fBgT/H3oZq9WuvRR5gPC70xpDiQU= -golang.org/x/net v0.48.0/go.mod h1:+ndRgGjkh8FGtu1w1FGbEC31if4VrNVMuKTgcAAnQRY= +golang.org/x/net v0.49.0 h1:eeHFmOGUTtaaPSGNmjBKpbng9MulQsJURQUAfUwY++o= +golang.org/x/net v0.49.0/go.mod h1:/ysNB2EvaqvesRkuLAyjI1ycPZlQHM3q01F02UY/MV8= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= diff --git a/pkg/inputunifi/remote.go b/pkg/inputunifi/remote.go index b34e3a48..a8886cb8 100644 --- a/pkg/inputunifi/remote.go +++ b/pkg/inputunifi/remote.go @@ -1,228 +1,29 @@ package inputunifi import ( - "crypto/tls" - "encoding/json" "fmt" - "io" - "net/http" - "net/url" "strings" - "time" + + "github.com/unpoller/unifi/v5" ) -const ( - remoteAPIBaseURL = "https://api.ui.com" - remoteAPIVersion = "v1" -) - -// Console represents a UniFi console from the remote API. -type Console struct { - ID string `json:"id"` - IPAddress string `json:"ipAddress"` - Type string `json:"type"` - Owner bool `json:"owner"` - IsBlocked bool `json:"isBlocked"` - ReportedState struct { - Name string `json:"name"` - Hostname string `json:"hostname"` - IP string `json:"ip"` - State string `json:"state"` - Mac string `json:"mac"` - } `json:"reportedState"` - ConsoleName string // Derived field: name from reportedState -} - -// HostsResponse represents the response from /v1/hosts endpoint. -type HostsResponse struct { - Data []Console `json:"data"` - HTTPStatusCode int `json:"httpStatusCode"` - TraceID string `json:"traceId"` - NextToken string `json:"nextToken,omitempty"` -} - -// Site represents a site from the remote API. -type RemoteSite struct { - ID string `json:"id"` - Name string `json:"name"` - Description string `json:"description"` -} - -// SitesResponse represents the response from the sites endpoint. -type SitesResponse struct { - Data []RemoteSite `json:"data"` - HTTPStatusCode int `json:"httpStatusCode"` - TraceID string `json:"traceId"` -} - -// remoteAPIClient handles HTTP requests to the remote UniFi API. -type remoteAPIClient struct { - apiKey string - baseURL string - client *http.Client - logError func(string, ...any) - logDebug func(string, ...any) - log func(string, ...any) -} - -// newRemoteAPIClient creates a new remote API client. -func (u *InputUnifi) newRemoteAPIClient(apiKey string) *remoteAPIClient { - if apiKey == "" { - return nil - } - +// discoverRemoteControllers discovers all controllers via remote API and creates Controller entries. +func (u *InputUnifi) discoverRemoteControllers(apiKey string) ([]*Controller, error) { // Handle file:// prefix for API key if strings.HasPrefix(apiKey, "file://") { apiKey = u.getPassFromFile(strings.TrimPrefix(apiKey, "file://")) } - return &remoteAPIClient{ - apiKey: apiKey, - baseURL: remoteAPIBaseURL, - client: &http.Client{ - Timeout: 30 * time.Second, - Transport: &http.Transport{ - TLSClientConfig: &tls.Config{ - InsecureSkipVerify: false, - }, - }, - }, - logError: u.LogErrorf, - logDebug: u.LogDebugf, - log: u.Logf, - } -} - -// makeRequest makes an HTTP request to the remote API. -func (c *remoteAPIClient) makeRequest(method, path string, queryParams map[string]string) ([]byte, error) { - fullURL := c.baseURL + path - - if len(queryParams) > 0 { - u, err := url.Parse(fullURL) - if err != nil { - return nil, fmt.Errorf("parsing URL: %w", err) - } - - q := u.Query() - for k, v := range queryParams { - q.Set(k, v) - } - u.RawQuery = q.Encode() - fullURL = u.String() - } - - c.logDebug("Making %s request to: %s", method, fullURL) - - req, err := http.NewRequest(method, fullURL, nil) - if err != nil { - return nil, fmt.Errorf("creating request: %w", err) - } - - req.Header.Set("Accept", "application/json") - req.Header.Set("X-API-Key", c.apiKey) - - resp, err := c.client.Do(req) - if err != nil { - return nil, fmt.Errorf("making request: %w", err) - } - defer resp.Body.Close() - - body, err := io.ReadAll(resp.Body) - if err != nil { - return nil, fmt.Errorf("reading response: %w", err) - } - - if resp.StatusCode < 200 || resp.StatusCode >= 300 { - return nil, fmt.Errorf("API request failed with status %d: %s", resp.StatusCode, string(body)) - } - - return body, nil -} - -// discoverConsoles discovers all consoles available via the remote API. -func (c *remoteAPIClient) discoverConsoles() ([]Console, error) { - // Start with first page - queryParams := map[string]string{ - "pageSize": "10", - } - - var allConsoles []Console - nextToken := "" - - for { - if nextToken != "" { - queryParams["nextToken"] = nextToken - } else { - // Remove nextToken from params for first request - delete(queryParams, "nextToken") - } - - body, err := c.makeRequest("GET", "/v1/hosts", queryParams) - if err != nil { - return nil, fmt.Errorf("fetching consoles: %w", err) - } - - var response HostsResponse - if err := json.Unmarshal(body, &response); err != nil { - return nil, fmt.Errorf("parsing consoles response: %w", err) - } - - // Filter for console type only - for _, console := range response.Data { - if console.Type == "console" && !console.IsBlocked { - // Extract the console name from reportedState - console.ConsoleName = console.ReportedState.Name - if console.ConsoleName == "" { - console.ConsoleName = console.ReportedState.Hostname - } - allConsoles = append(allConsoles, console) - } - } - - // Check if there's a nextToken to continue pagination - if response.NextToken == "" { - break - } - - nextToken = response.NextToken - c.logDebug("Fetching next page of consoles with nextToken: %s", nextToken) - } - - return allConsoles, nil -} - -// discoverSites discovers all sites for a given console ID. -func (c *remoteAPIClient) discoverSites(consoleID string) ([]RemoteSite, error) { - path := fmt.Sprintf("/v1/connector/consoles/%s/proxy/network/integration/v1/sites", consoleID) - - queryParams := map[string]string{ - "offset": "0", - "limit": "100", - } - - body, err := c.makeRequest("GET", path, queryParams) - if err != nil { - return nil, fmt.Errorf("fetching sites for console %s: %w", consoleID, err) - } - - var response SitesResponse - if err := json.Unmarshal(body, &response); err != nil { - return nil, fmt.Errorf("parsing sites response: %w", err) - } - - return response.Data, nil -} - -// discoverRemoteControllers discovers all controllers via remote API and creates Controller entries. -func (u *InputUnifi) discoverRemoteControllers(apiKey string) ([]*Controller, error) { - client := u.newRemoteAPIClient(apiKey) - if client == nil { + if apiKey == "" { return nil, fmt.Errorf("remote API key not provided") } + // Use library client + client := unifi.NewRemoteAPIClient(apiKey, u.LogErrorf, u.LogDebugf, u.Logf) + u.Logf("Discovering remote UniFi consoles...") - consoles, err := client.discoverConsoles() + consoles, err := client.DiscoverConsoles() if err != nil { return nil, fmt.Errorf("discovering consoles: %w", err) } @@ -246,7 +47,7 @@ func (u *InputUnifi) discoverRemoteControllers(apiKey string) ([]*Controller, er } u.LogDebugf("Discovering sites for console: %s (%s)", console.ID, consoleName) - sites, err := client.discoverSites(console.ID) + sites, err := client.DiscoverSites(console.ID) if err != nil { u.LogErrorf("Failed to discover sites for console %s: %v", console.ID, err) continue @@ -268,7 +69,7 @@ func (u *InputUnifi) discoverRemoteControllers(apiKey string) ([]*Controller, er // Set URL to connector base - the library appends /proxy/network/status // But for integration API we need /proxy/network/integration/v1/... // This may require library updates, but try connector base first - URL: fmt.Sprintf("%s/v1/connector/consoles/%s", remoteAPIBaseURL, console.ID), + URL: fmt.Sprintf("%s/v1/connector/consoles/%s", unifi.RemoteAPIBaseURL, console.ID), } // Ensure defaults are set before calling setControllerDefaults