move remote.go to use unifi library functions

This commit is contained in:
brngates98
2026-01-25 08:59:11 -05:00
parent 0cb331a745
commit e17d8bf62e
3 changed files with 15 additions and 216 deletions

4
go.mod
View File

@@ -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

6
go.sum
View File

@@ -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=

View File

@@ -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