Refactor: Integrate regional agents in service-operation
Integrate distributed monitoring agents for HTTP, PING, TCP, and DNS checks
This commit is contained in:
@@ -0,0 +1,117 @@
|
||||
|
||||
package pocketbase
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/http"
|
||||
"time"
|
||||
)
|
||||
|
||||
func (c *PocketBaseClient) GetRegionalServices() ([]RegionalService, error) {
|
||||
resp, err := c.httpClient.Get(fmt.Sprintf("%s/api/collections/regional_service/records", c.baseURL))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
if resp.StatusCode == http.StatusForbidden {
|
||||
// Collection might require authentication or doesn't exist
|
||||
fmt.Printf("Warning: Cannot access regional_service collection (403). Regional monitoring will be disabled.\n")
|
||||
return nil, fmt.Errorf("access denied to regional_service collection")
|
||||
}
|
||||
|
||||
if resp.StatusCode != http.StatusOK {
|
||||
return nil, fmt.Errorf("failed to get regional services: status %d", resp.StatusCode)
|
||||
}
|
||||
|
||||
var response RegionalServicesResponse
|
||||
if err := c.parseResponse(resp, &response); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return response.Items, nil
|
||||
}
|
||||
|
||||
func (c *PocketBaseClient) GetDefaultRegionalService() (*RegionalService, error) {
|
||||
services, err := c.GetRegionalServices()
|
||||
if err != nil {
|
||||
// Try to create a default regional service if we can't get existing ones
|
||||
return c.CreateDefaultRegionalService()
|
||||
}
|
||||
|
||||
// Look for a service with region_name "Default"
|
||||
for _, service := range services {
|
||||
if service.RegionName == "Default" {
|
||||
return &service, nil
|
||||
}
|
||||
}
|
||||
|
||||
// If no default service found, try to create one
|
||||
return c.CreateDefaultRegionalService()
|
||||
}
|
||||
|
||||
func (c *PocketBaseClient) CreateDefaultRegionalService() (*RegionalService, error) {
|
||||
defaultService := map[string]interface{}{
|
||||
"region_name": "Default",
|
||||
"status": "active",
|
||||
"agent_id": "1",
|
||||
"agent_ip_address": "127.0.0.1",
|
||||
"connection": "offline",
|
||||
"token": fmt.Sprintf("default-%d", time.Now().Unix()),
|
||||
}
|
||||
|
||||
err := c.createRecord("regional_service", defaultService)
|
||||
if err != nil {
|
||||
fmt.Printf("Warning: Could not create default regional service: %v\n", err)
|
||||
// Return a fallback service for local operations
|
||||
return &RegionalService{
|
||||
ID: "default",
|
||||
RegionName: "Default",
|
||||
Status: "active",
|
||||
AgentID: "1",
|
||||
AgentIPAddress: "127.0.0.1",
|
||||
Connection: "offline",
|
||||
Token: "default-token",
|
||||
}, nil
|
||||
}
|
||||
|
||||
// Try to fetch the created service
|
||||
services, err := c.GetRegionalServices()
|
||||
if err == nil {
|
||||
for _, service := range services {
|
||||
if service.RegionName == "Default" {
|
||||
return &service, nil
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Return fallback if still can't get the created service
|
||||
return &RegionalService{
|
||||
ID: "default",
|
||||
RegionName: "Default",
|
||||
Status: "active",
|
||||
AgentID: "1",
|
||||
AgentIPAddress: "127.0.0.1",
|
||||
Connection: "offline",
|
||||
Token: "default-token",
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (c *PocketBaseClient) UpdateRegionalServiceConnection(serviceID, connection string) error {
|
||||
// Determine status based on connection
|
||||
status := "inactive"
|
||||
if connection == "online" {
|
||||
status = "active"
|
||||
}
|
||||
|
||||
data := map[string]interface{}{
|
||||
"connection": connection,
|
||||
"status": status,
|
||||
}
|
||||
|
||||
return c.updateRecord("regional_service", serviceID, data)
|
||||
}
|
||||
|
||||
func (c *PocketBaseClient) SaveSSLData(sslData SSLDataRecord) error {
|
||||
return c.createRecord("ssl_data", sslData)
|
||||
}
|
||||
@@ -1,4 +1,3 @@
|
||||
|
||||
package pocketbase
|
||||
|
||||
import "time"
|
||||
@@ -46,6 +45,8 @@ type PingDataRecord struct {
|
||||
RTTs string `json:"rtts"`
|
||||
Details string `json:"details,omitempty"`
|
||||
ErrorMessage string `json:"error_message,omitempty"`
|
||||
RegionName string `json:"region_name,omitempty"`
|
||||
AgentID string `json:"agent_id,omitempty"`
|
||||
}
|
||||
|
||||
type UptimeDataRecord struct {
|
||||
@@ -61,6 +62,8 @@ type UptimeDataRecord struct {
|
||||
Details string `json:"details"`
|
||||
Region string `json:"region,omitempty"`
|
||||
RegionID string `json:"region_id,omitempty"`
|
||||
RegionName string `json:"region_name,omitempty"`
|
||||
AgentID string `json:"agent_id,omitempty"`
|
||||
}
|
||||
|
||||
type DNSDataRecord struct {
|
||||
@@ -94,6 +97,46 @@ type TCPDataRecord struct {
|
||||
AgentID string `json:"agent_id,omitempty"`
|
||||
}
|
||||
|
||||
// SSL Data Record remains unchanged - no regional agent fields
|
||||
type SSLDataRecord struct {
|
||||
ServiceID string `json:"service_id"`
|
||||
Timestamp time.Time `json:"timestamp"`
|
||||
ResponseTime int64 `json:"response_time"`
|
||||
Status string `json:"status"`
|
||||
ValidFrom string `json:"valid_from"`
|
||||
ValidTill string `json:"valid_till"`
|
||||
DaysLeft int `json:"days_left"`
|
||||
Issuer string `json:"issuer"`
|
||||
Subject string `json:"subject"`
|
||||
SerialNumber string `json:"serial_number"`
|
||||
Algorithm string `json:"algorithm"`
|
||||
SANs string `json:"sans"`
|
||||
ResolvedIP string `json:"resolved_ip"`
|
||||
ErrorMessage string `json:"error_message,omitempty"`
|
||||
Details string `json:"details,omitempty"`
|
||||
}
|
||||
|
||||
// Regional Service record structure
|
||||
type RegionalService struct {
|
||||
ID string `json:"id"`
|
||||
RegionName string `json:"region_name"`
|
||||
Status string `json:"status"`
|
||||
AgentID string `json:"agent_id"`
|
||||
AgentIPAddress string `json:"agent_ip_address"`
|
||||
Connection string `json:"connection"`
|
||||
Token string `json:"token"`
|
||||
Created string `json:"created"`
|
||||
Updated string `json:"updated"`
|
||||
}
|
||||
|
||||
type RegionalServicesResponse struct {
|
||||
Page int `json:"page"`
|
||||
PerPage int `json:"perPage"`
|
||||
TotalItems int `json:"totalItems"`
|
||||
TotalPages int `json:"totalPages"`
|
||||
Items []RegionalService `json:"items"`
|
||||
}
|
||||
|
||||
type Service struct {
|
||||
ID string `json:"id"`
|
||||
Name string `json:"name"`
|
||||
|
||||
@@ -9,6 +9,50 @@ import (
|
||||
"net/http"
|
||||
)
|
||||
|
||||
func (c *PocketBaseClient) parseResponse(resp *http.Response, target interface{}) error {
|
||||
body, err := io.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return json.Unmarshal(body, target)
|
||||
}
|
||||
|
||||
func (c *PocketBaseClient) updateRecord(collection string, recordID string, data interface{}) error {
|
||||
jsonData, err := json.Marshal(data)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
req, err := http.NewRequest("PATCH",
|
||||
fmt.Sprintf("%s/api/collections/%s/records/%s", c.baseURL, collection, recordID),
|
||||
bytes.NewBuffer(jsonData))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
req.Header.Set("Content-Type", "application/json")
|
||||
|
||||
resp, err := c.httpClient.Do(req)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
if resp.StatusCode != http.StatusOK {
|
||||
bodyBytes, _ := io.ReadAll(resp.Body)
|
||||
bodyString := string(bodyBytes)
|
||||
|
||||
fmt.Printf("Failed to update record in %s collection. Status: %d, Response: %s\n",
|
||||
collection, resp.StatusCode, bodyString)
|
||||
|
||||
return fmt.Errorf("failed to update record in %s, status: %d, response: %s",
|
||||
collection, resp.StatusCode, bodyString)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *PocketBaseClient) createRecord(collection string, data interface{}) error {
|
||||
jsonData, err := json.Marshal(data)
|
||||
if err != nil {
|
||||
|
||||
Reference in New Issue
Block a user