package main import ( "bytes" "encoding/json" "flag" "fmt" "io" "net/http" "os" "os/exec" "path/filepath" "time" ) type report struct { NodeID string `json:"nodeId"` Kind string `json:"kind"` Address string `json:"address,omitempty"` Version string `json:"version"` Status string `json:"status"` Metadata map[string]any `json:"metadata"` } func main() { listen := flag.String("listen", ":8091", "HTTP listen address") source := flag.String("source", ".", "directory permitted for replication") flag.Parse() root, err := filepath.Abs(*source) if err != nil { panic(err) } go heartbeat(root) http.HandleFunc("/healthz", func(w http.ResponseWriter, _ *http.Request) { w.WriteHeader(http.StatusNoContent) }) http.HandleFunc("/replicate", func(w http.ResponseWriter, r *http.Request) { if r.Method != http.MethodPost { http.Error(w, "POST required", http.StatusMethodNotAllowed) return } var request struct { Host string `json:"host"` Destination string `json:"destination"` } if json.NewDecoder(r.Body).Decode(&request) != nil || request.Host == "" || request.Destination == "" || filepath.IsAbs(request.Destination) || filepath.Clean(request.Destination) == ".." { http.Error(w, "host and relative destination required", http.StatusBadRequest) return } output, err := exec.Command("scp", "-r", root+"/.", request.Host+":"+request.Destination).CombinedOutput() if err != nil { http.Error(w, string(output), http.StatusBadGateway) return } w.WriteHeader(http.StatusAccepted) }) fmt.Println(http.ListenAndServe(*listen, nil)) } func heartbeat(root string) { central := os.Getenv("CENTRAL_URL") if central == "" { return } host, _ := os.Hostname() for { body, _ := json.Marshal(report{NodeID: host, Kind: "replica", Address: os.Getenv("PEER_PUBLIC_URL"), Version: "0.1.0", Status: "online", Metadata: map[string]any{"source": root}}) request, _ := http.NewRequest(http.MethodPost, central+"/api/peers/heartbeat", bytes.NewReader(body)) request.Header.Set("Content-Type", "application/json") if token := os.Getenv("PEER_API_TOKEN"); token != "" { request.Header.Set("Authorization", "Bearer "+token) } response, err := http.DefaultClient.Do(request) if err == nil { _, _ = io.Copy(io.Discard, response.Body) _ = response.Body.Close() } time.Sleep(time.Minute) } }