Implement CheckCle Microservice Operation
A Go-based microservice for service operations including ICMP ping, DNS resolution, and TCP connectivity
This commit is contained in:
@@ -0,0 +1,52 @@
|
||||
|
||||
package pocketbase
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/http"
|
||||
"time"
|
||||
)
|
||||
|
||||
type PocketBaseClient struct {
|
||||
baseURL string
|
||||
httpClient *http.Client
|
||||
}
|
||||
|
||||
func NewPocketBaseClient(baseURL string) (*PocketBaseClient, error) {
|
||||
// Use provided baseURL or default to localhost
|
||||
if baseURL == "" {
|
||||
baseURL = "http://localhost:8090"
|
||||
}
|
||||
|
||||
client := &PocketBaseClient{
|
||||
baseURL: baseURL,
|
||||
httpClient: &http.Client{
|
||||
Timeout: 30 * time.Second,
|
||||
},
|
||||
}
|
||||
|
||||
return client, nil
|
||||
}
|
||||
|
||||
func (c *PocketBaseClient) GetBaseURL() string {
|
||||
return c.baseURL
|
||||
}
|
||||
|
||||
func (c *PocketBaseClient) TestConnection() error {
|
||||
resp, err := c.httpClient.Get(fmt.Sprintf("%s/api/health", c.baseURL))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
if resp.StatusCode != http.StatusOK {
|
||||
return fmt.Errorf("PocketBase health check failed with status: %d", resp.StatusCode)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *PocketBaseClient) IsAuthenticated() bool {
|
||||
// Since we're using public access mode, always return true
|
||||
return true
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
|
||||
package pocketbase
|
||||
|
||||
func (c *PocketBaseClient) SaveMetrics(metrics MetricsRecord) error {
|
||||
return c.createRecord("services_metrics", metrics)
|
||||
}
|
||||
|
||||
func (c *PocketBaseClient) SavePingData(pingData PingDataRecord) error {
|
||||
return c.createRecord("ping_data", pingData)
|
||||
}
|
||||
|
||||
func (c *PocketBaseClient) SaveUptimeData(uptimeData UptimeDataRecord) error {
|
||||
return c.createRecord("uptime_data", uptimeData)
|
||||
}
|
||||
|
||||
func (c *PocketBaseClient) SaveDNSData(dnsData DNSDataRecord) error {
|
||||
return c.createRecord("dns_data", dnsData)
|
||||
}
|
||||
|
||||
func (c *PocketBaseClient) SaveTCPData(tcpData TCPDataRecord) error {
|
||||
return c.createRecord("tcp_data", tcpData)
|
||||
}
|
||||
@@ -0,0 +1,138 @@
|
||||
|
||||
package pocketbase
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"time"
|
||||
)
|
||||
|
||||
func (c *PocketBaseClient) GetServices() ([]Service, error) {
|
||||
req, err := http.NewRequest("GET",
|
||||
fmt.Sprintf("%s/api/collections/services/records", c.baseURL), nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// No authentication header needed for public access
|
||||
resp, err := c.httpClient.Do(req)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
if resp.StatusCode != http.StatusOK {
|
||||
return nil, fmt.Errorf("failed to fetch services, status: %d", resp.StatusCode)
|
||||
}
|
||||
|
||||
var servicesResponse ServicesResponse
|
||||
if err := json.NewDecoder(resp.Body).Decode(&servicesResponse); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return servicesResponse.Items, nil
|
||||
}
|
||||
|
||||
func (c *PocketBaseClient) GetService(serviceID string) (*Service, error) {
|
||||
req, err := http.NewRequest("GET",
|
||||
fmt.Sprintf("%s/api/collections/services/records/%s", c.baseURL, serviceID), nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// No authentication header needed for public access
|
||||
resp, err := c.httpClient.Do(req)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
if resp.StatusCode != http.StatusOK {
|
||||
return nil, fmt.Errorf("failed to fetch service %s, status: %d", serviceID, resp.StatusCode)
|
||||
}
|
||||
|
||||
var service Service
|
||||
if err := json.NewDecoder(resp.Body).Decode(&service); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &service, nil
|
||||
}
|
||||
|
||||
func (c *PocketBaseClient) GetActiveServices() ([]Service, error) {
|
||||
// Only fetch services that are not paused
|
||||
req, err := http.NewRequest("GET",
|
||||
fmt.Sprintf("%s/api/collections/services/records?filter=(status!='paused')", c.baseURL), nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// No authentication header needed for public access
|
||||
resp, err := c.httpClient.Do(req)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
if resp.StatusCode != http.StatusOK {
|
||||
return nil, fmt.Errorf("failed to fetch active services, status: %d", resp.StatusCode)
|
||||
}
|
||||
|
||||
var servicesResponse ServicesResponse
|
||||
if err := json.NewDecoder(resp.Body).Decode(&servicesResponse); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return servicesResponse.Items, nil
|
||||
}
|
||||
|
||||
func (c *PocketBaseClient) UpdateServiceStatus(serviceID string, status string, responseTime int64, errorMessage string) error {
|
||||
// First check if the service is paused before updating
|
||||
service, err := c.GetService(serviceID)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to check service status before update: %v", err)
|
||||
}
|
||||
|
||||
if service.Status == "paused" {
|
||||
return fmt.Errorf("service %s is paused, skipping status update", serviceID)
|
||||
}
|
||||
|
||||
updateData := map[string]interface{}{
|
||||
"status": status,
|
||||
"response_time": responseTime,
|
||||
"last_checked": time.Now().Format(time.RFC3339),
|
||||
}
|
||||
|
||||
if errorMessage != "" {
|
||||
updateData["error_message"] = errorMessage
|
||||
}
|
||||
|
||||
jsonData, err := json.Marshal(updateData)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
req, err := http.NewRequest("PATCH",
|
||||
fmt.Sprintf("%s/api/collections/services/records/%s", c.baseURL, serviceID),
|
||||
bytes.NewBuffer(jsonData))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// No authentication header needed
|
||||
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 && resp.StatusCode != http.StatusNoContent {
|
||||
return fmt.Errorf("failed to update service status, status: %d", resp.StatusCode)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
@@ -0,0 +1,126 @@
|
||||
|
||||
package pocketbase
|
||||
|
||||
import "time"
|
||||
|
||||
type AuthResponse struct {
|
||||
Token string `json:"token"`
|
||||
Record interface{} `json:"record"`
|
||||
}
|
||||
|
||||
type MetricsRecord struct {
|
||||
ServiceName string `json:"service_name"`
|
||||
Host string `json:"host"`
|
||||
Uptime float64 `json:"uptime"`
|
||||
ResponseTime int64 `json:"response_time"`
|
||||
LastChecked string `json:"last_checked"`
|
||||
Port int `json:"port,omitempty"`
|
||||
Domain string `json:"domain,omitempty"`
|
||||
HeartbeatInterval int `json:"heartbeat_interval,omitempty"`
|
||||
MaxRetries int `json:"max_retries,omitempty"`
|
||||
NotificationID string `json:"notification_id,omitempty"`
|
||||
TemplateID string `json:"template_id,omitempty"`
|
||||
ServiceType string `json:"service_type"`
|
||||
Status string `json:"status"`
|
||||
URL string `json:"url,omitempty"`
|
||||
Alerts string `json:"alerts,omitempty"`
|
||||
StatusCodes string `json:"status_codes,omitempty"`
|
||||
Keyword string `json:"keyword,omitempty"`
|
||||
ErrorMessage string `json:"error_message,omitempty"`
|
||||
Details string `json:"details,omitempty"`
|
||||
CheckedAt string `json:"checked_at"`
|
||||
}
|
||||
|
||||
type PingDataRecord struct {
|
||||
ServiceID string `json:"service_id"`
|
||||
Timestamp time.Time `json:"timestamp"`
|
||||
ResponseTime int64 `json:"response_time"`
|
||||
Status string `json:"status"`
|
||||
PacketLoss string `json:"packet_loss"`
|
||||
Latency string `json:"latency"`
|
||||
MaxRTT string `json:"max_rtt"`
|
||||
MinRTT string `json:"min_rtt"`
|
||||
PacketsSent string `json:"packets_sent"`
|
||||
PacketsRecv string `json:"packets_recv"`
|
||||
AvgRTT string `json:"avg_rtt"`
|
||||
RTTs string `json:"rtts"`
|
||||
Details string `json:"details,omitempty"`
|
||||
ErrorMessage string `json:"error_message,omitempty"`
|
||||
}
|
||||
|
||||
type UptimeDataRecord struct {
|
||||
ServiceID string `json:"service_id"`
|
||||
Timestamp time.Time `json:"timestamp"`
|
||||
ResponseTime int64 `json:"response_time"`
|
||||
Status string `json:"status"`
|
||||
Packets string `json:"packets"`
|
||||
Latency string `json:"latency"`
|
||||
StatusCodes string `json:"status_codes"`
|
||||
Keyword string `json:"keyword"`
|
||||
ErrorMessage string `json:"error_message"`
|
||||
Details string `json:"details"`
|
||||
Region string `json:"region,omitempty"`
|
||||
RegionID string `json:"region_id,omitempty"`
|
||||
}
|
||||
|
||||
type DNSDataRecord struct {
|
||||
ServiceID string `json:"service_id"`
|
||||
Timestamp time.Time `json:"timestamp"`
|
||||
ResponseTime int64 `json:"response_time"`
|
||||
Status string `json:"status"`
|
||||
QueryType string `json:"query_type"`
|
||||
ResolveIP string `json:"resolve_ip"`
|
||||
MsgSize string `json:"msg_size"`
|
||||
Question string `json:"question"`
|
||||
Answer string `json:"answer"`
|
||||
Authority string `json:"authority"`
|
||||
ErrorMessage string `json:"error_message,omitempty"`
|
||||
Details string `json:"details,omitempty"`
|
||||
RegionName string `json:"region_name,omitempty"`
|
||||
AgentID string `json:"agent_id,omitempty"`
|
||||
}
|
||||
|
||||
type TCPDataRecord struct {
|
||||
ServiceID string `json:"service_id"`
|
||||
Timestamp time.Time `json:"timestamp"`
|
||||
ResponseTime int64 `json:"response_time"`
|
||||
Status string `json:"status"`
|
||||
Connection string `json:"connection"`
|
||||
Latency string `json:"latency"`
|
||||
Port string `json:"port"`
|
||||
ErrorMessage string `json:"error_message,omitempty"`
|
||||
Details string `json:"details,omitempty"`
|
||||
RegionName string `json:"region_name,omitempty"`
|
||||
AgentID string `json:"agent_id,omitempty"`
|
||||
}
|
||||
|
||||
type Service struct {
|
||||
ID string `json:"id"`
|
||||
Name string `json:"name"`
|
||||
Host string `json:"host"`
|
||||
Uptime float64 `json:"uptime"`
|
||||
ResponseTime int64 `json:"response_time"`
|
||||
LastChecked string `json:"last_checked"`
|
||||
Port int `json:"port"`
|
||||
Domain string `json:"domain"`
|
||||
HeartbeatInterval int `json:"heartbeat_interval"`
|
||||
MaxRetries int `json:"max_retries"`
|
||||
NotificationID string `json:"notification_id"`
|
||||
TemplateID string `json:"template_id"`
|
||||
ServiceType string `json:"service_type"`
|
||||
Status string `json:"status"`
|
||||
URL string `json:"url"`
|
||||
Alerts string `json:"alerts"`
|
||||
StatusCodes string `json:"status_codes"`
|
||||
Keyword string `json:"keyword"`
|
||||
Created string `json:"created"`
|
||||
Updated string `json:"updated"`
|
||||
}
|
||||
|
||||
type ServicesResponse struct {
|
||||
Page int `json:"page"`
|
||||
PerPage int `json:"perPage"`
|
||||
TotalItems int `json:"totalItems"`
|
||||
TotalPages int `json:"totalPages"`
|
||||
Items []Service `json:"items"`
|
||||
}
|
||||
@@ -0,0 +1,47 @@
|
||||
|
||||
package pocketbase
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
"net/http"
|
||||
)
|
||||
|
||||
func (c *PocketBaseClient) createRecord(collection string, data interface{}) error {
|
||||
jsonData, err := json.Marshal(data)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
req, err := http.NewRequest("POST",
|
||||
fmt.Sprintf("%s/api/collections/%s/records", c.baseURL, collection),
|
||||
bytes.NewBuffer(jsonData))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// No authentication header needed
|
||||
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 && resp.StatusCode != http.StatusCreated {
|
||||
// Read response body for better error details
|
||||
bodyBytes, _ := io.ReadAll(resp.Body)
|
||||
bodyString := string(bodyBytes)
|
||||
|
||||
fmt.Printf("Failed to create record in %s collection. Status: %d, Response: %s\n",
|
||||
collection, resp.StatusCode, bodyString)
|
||||
|
||||
return fmt.Errorf("failed to create record in %s, status: %d, response: %s",
|
||||
collection, resp.StatusCode, bodyString)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
Reference in New Issue
Block a user