BackGo/pkg/backup/interface.go

187 lines
5.9 KiB
Go
Raw Normal View History

package backup
import (
2024-03-28 00:48:22 -06:00
"net"
"crypto/tls"
2024-03-28 00:48:22 -06:00
"encoding/json"
"fmt"
"log"
"net/http"
"golang.org/x/crypto/acme/autocert"
2024-03-28 00:48:22 -06:00
"io"
"bytes"
)
2024-03-28 00:48:22 -06:00
type CommandPayload struct {
Action string `json:"action"`
Node string `json:"node"`
Value string `json:"value"`
}
type JoinRequest struct {
NodeHostname string `json:"nodeHostname"`
Token string `json:"token"`
IP string `json:"ip"`
}
// Method to create a token
func (manager *DBManager) CreateToken(hostname, token string) error {
_, err := manager.DB.Exec("INSERT INTO tokens (hostname, token) VALUES (?, ?)", hostname, token)
if err != nil {
log.Printf("Failed to insert token: %v", err)
return err
}
return nil
}
// Method to verify if the hostname resolves to the expected IP
func (manager *DBManager) VerifyHostname(hostname, expectedIP string) bool {
ips, err := net.LookupIP(hostname)
if err != nil {
log.Printf("Failed to lookup IP for hostname %s: %v", hostname, err)
return false
}
for _, ip := range ips {
if ip.String() == expectedIP {
return true
}
}
return false
}
// GenerateCertForDomain generates an SSL certificate for a domain using Let's Encrypt
func GenerateCertForDomain(domain string) {
certManager := autocert.Manager{
Prompt: autocert.AcceptTOS,
Cache: autocert.DirCache("certs"), // Stores certificates in ./certs
2024-03-28 00:56:45 -06:00
HostPolicy: autocert.HostWhitelist(domain),
}
server := &http.Server{
Addr: ":https", // Default HTTPS port
TLSConfig: &tls.Config{
GetCertificate: certManager.GetCertificate,
},
}
// Redirect HTTP to HTTPS
go http.ListenAndServe(":http", certManager.HTTPHandler(nil))
log.Printf("Starting HTTPS server for %s\n", domain)
log.Fatal(server.ListenAndServeTLS("", "")) // Keys are provided by Let's Encrypt
}
2024-03-28 00:48:22 -06:00
var dbManager *DBManager
// HandleJoinRequest handles a join request from a node
func HandleJoinRequest(w http.ResponseWriter, r *http.Request) {
2024-03-28 00:48:22 -06:00
if r.Method != http.MethodPost {
http.Error(w, "Only POST method is allowed", http.StatusMethodNotAllowed)
return
}
var req JoinRequest
if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
http.Error(w, "Error decoding request body", http.StatusBadRequest)
return
}
// Extract the IP address from r.RemoteAddr
remoteIP, _, err := net.SplitHostPort(r.RemoteAddr)
if err != nil {
http.Error(w, "Failed to parse remote IP", http.StatusBadRequest)
return
}
// Verify the token exists in the database and the hostname resolves to the connecting IP
if !dbManager.VerifyToken(req.Token) || !dbManager.VerifyHostname(req.NodeHostname, remoteIP) {
http.Error(w, "Invalid token or hostname does not resolve to the connecting IP", http.StatusUnauthorized)
return
}
fmt.Fprintf(w, "Join request for %s with token %s accepted\n", req.NodeHostname, req.Token)
}
// SendCommandToNode sends a CLI command to a specified node in the cluster
2024-03-28 00:48:22 -06:00
func SendCommandToNode(nodeAddress, action, nodeName, value string) {
// Construct the command payload
payload := CommandPayload{
Action: action,
Node: nodeName,
Value: value,
}
// Marshal the payload into JSON
payloadBytes, err := json.Marshal(payload)
if err != nil {
log.Printf("Error marshaling command payload: %v", err)
return
}
// Send the command to the node
resp, err := http.Post(fmt.Sprintf("http://%s/api/command", nodeAddress), "application/json", bytes.NewReader(payloadBytes))
if err != nil {
log.Printf("Error sending command to node %s: %v", nodeName, err)
return
}
defer resp.Body.Close()
// Note: As of Go 1.16, ioutil.ReadAll is deprecated. Use io.ReadAll instead.
responseBody, err := io.ReadAll(resp.Body)
if err != nil {
log.Printf("Error reading response from node %s: %v", nodeName, err)
return
}
fmt.Printf("Command sent to node %s, response: %s\n", nodeName, string(responseBody))
}
// StartMasterServer starts the master API server that listens for join requests and other commands
func StartMasterServer(domain string) {
2024-03-28 00:48:22 -06:00
go SetupWebServer() // Start the API server in a separate goroutine
// Generate SSL certificate and start HTTPS server
GenerateCertForDomain(domain)
}
2024-03-28 00:48:22 -06:00
// SetupWebServer initializes and starts the web server for handling API requests.
func SetupWebServer() {
// Define your HTTP request handlers
http.HandleFunc("/api/join", HandleJoinRequest) // Handle join requests
http.HandleFunc("/api/command", HandleCommand) // Handle commands sent to nodes
// More API endpoints can be added here
fmt.Println("Starting API server on port 8080...")
if err := http.ListenAndServe(":8080", nil); err != nil {
log.Fatalf("Failed to start API server: %v", err)
}
}
// HandleCommand processes commands sent to nodes.
func HandleCommand(w http.ResponseWriter, r *http.Request) {
if r.Method != "POST" {
http.Error(w, "Invalid request method", http.StatusMethodNotAllowed)
return
}
2024-03-28 00:56:45 -06:00
// Extracting command from the request body
2024-03-28 00:48:22 -06:00
// You'll need to define the structure of your request body
var req struct {
NodeAddress string `json:"nodeAddress"`
Command string `json:"command"`
}
if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
http.Error(w, "Error decoding request body", http.StatusBadRequest)
return
}
// Send the command to the specified node
fmt.Printf("Sending command '%s' to node %s\n", req.Command, req.NodeAddress)
// Implement the logic to send the command to the node here
// Respond to the request indicating success
w.WriteHeader(http.StatusOK)
fmt.Fprintf(w, "Command sent successfully to node %s\n", req.NodeAddress)
}