This commit is contained in:
partisan 2024-07-05 03:08:35 +02:00
parent 9f5ef7a1ef
commit f769f70ce7
5 changed files with 237 additions and 36 deletions

73
init.go
View file

@ -10,14 +10,17 @@ import (
"os" "os"
"strconv" "strconv"
"strings" "strings"
"sync"
"github.com/fsnotify/fsnotify"
) )
// Configuration structure // Configuration structure
type Config struct { type Config struct {
Port int Port int
ConnectionCode string AuthCode string
Peers []string Peers []string
OpenSearch OpenSearchConfig OpenSearch OpenSearchConfig
} }
type OpenSearchConfig struct { type OpenSearchConfig struct {
@ -34,18 +37,31 @@ var defaultConfig = Config{
const configFilePath = "config.json" const configFilePath = "config.json"
var config Config
var configLock sync.RWMutex
func main() { func main() {
// Run the initialization process
err := initConfig() err := initConfig()
if err != nil { if err != nil {
fmt.Println("Error during initialization:", err) fmt.Println("Error during initialization:", err)
return return
} }
// This is stupid
loadNodeConfig() loadNodeConfig()
go startFileWatcher()
go checkMasterHeartbeat()
if config.AuthCode == "" {
config.AuthCode = generateStrongRandomString(64)
fmt.Printf("Generated connection code: %s\n", config.AuthCode)
saveConfig(config)
}
if len(config.Peers) > 0 {
go startNodeClient(config.Peers)
startElection()
}
// Start the main application
runServer() runServer()
} }
@ -55,6 +71,7 @@ func initConfig() error {
} }
fmt.Println("Configuration file already exists.") fmt.Println("Configuration file already exists.")
config = loadConfig()
return nil return nil
} }
@ -94,9 +111,9 @@ func createConfig() error {
} }
} }
if config.ConnectionCode == "" { if config.AuthCode == "" {
config.ConnectionCode = generateStrongRandomString(32) config.AuthCode = generateStrongRandomString(64)
fmt.Printf("Generated connection code: %s\n", config.ConnectionCode) fmt.Printf("Generated connection code: %s\n", config.AuthCode)
} }
saveConfig(config) saveConfig(config)
@ -146,3 +163,39 @@ func generateStrongRandomString(length int) string {
} }
return base64.URLEncoding.EncodeToString(bytes)[:length] return base64.URLEncoding.EncodeToString(bytes)[:length]
} }
func startFileWatcher() {
watcher, err := fsnotify.NewWatcher()
if err != nil {
log.Fatal(err)
}
go func() {
defer watcher.Close()
for {
select {
case event, ok := <-watcher.Events:
if !ok {
return
}
if event.Op&fsnotify.Write == fsnotify.Write {
log.Println("Modified file:", event.Name)
configLock.Lock()
config = loadConfig()
configLock.Unlock()
// Perform your logic here to handle the changes in the config file
}
case err, ok := <-watcher.Errors:
if !ok {
return
}
log.Println("Error:", err)
}
}
}()
err = watcher.Add(configFilePath)
if err != nil {
log.Fatal(err)
}
}

24
main.go
View file

@ -122,18 +122,18 @@ func parsePageParameter(pageStr string) int {
} }
func runServer() { func runServer() {
http.Handle("/static/", http.StripPrefix("/static/", http.FileServer(http.Dir("static")))) // http.Handle("/static/", http.StripPrefix("/static/", http.FileServer(http.Dir("static"))))
http.HandleFunc("/", handleSearch) // http.HandleFunc("/", handleSearch)
http.HandleFunc("/search", handleSearch) // http.HandleFunc("/search", handleSearch)
http.HandleFunc("/img_proxy", handleImageProxy) // http.HandleFunc("/img_proxy", handleImageProxy)
http.HandleFunc("/settings", func(w http.ResponseWriter, r *http.Request) { // http.HandleFunc("/settings", func(w http.ResponseWriter, r *http.Request) {
http.ServeFile(w, r, "templates/settings.html") // http.ServeFile(w, r, "templates/settings.html")
}) // })
http.HandleFunc("/opensearch.xml", func(w http.ResponseWriter, r *http.Request) { // http.HandleFunc("/opensearch.xml", func(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "application/opensearchdescription+xml") // w.Header().Set("Content-Type", "application/opensearchdescription+xml")
http.ServeFile(w, r, "static/opensearch.xml") // http.ServeFile(w, r, "static/opensearch.xml")
}) // })
initializeTorrentSites() // initializeTorrentSites()
http.HandleFunc("/node", handleNodeRequest) // Handle node requests http.HandleFunc("/node", handleNodeRequest) // Handle node requests

81
node-master.go Normal file
View file

@ -0,0 +1,81 @@
package main
import (
"log"
"sync"
"time"
)
var (
isMaster bool
masterNode string
masterNodeMux sync.RWMutex
)
const (
heartbeatInterval = 5 * time.Second
heartbeatTimeout = 15 * time.Second
electionTimeout = 10 * time.Second
)
func sendHeartbeats() {
for {
if !isMaster {
return
}
for _, node := range peers {
err := sendMessage(node, authCode, "heartbeat", authCode)
if err != nil {
log.Printf("Error sending heartbeat to %s: %v", node, err)
}
}
time.Sleep(heartbeatInterval)
}
}
func checkMasterHeartbeat() {
for {
time.Sleep(heartbeatTimeout)
masterNodeMux.RLock()
if masterNode == authCode || masterNode == "" {
masterNodeMux.RUnlock()
continue
}
masterNodeMux.RUnlock()
masterNodeMux.Lock()
masterNode = ""
masterNodeMux.Unlock()
startElection()
}
}
func startElection() {
masterNodeMux.Lock()
defer masterNodeMux.Unlock()
for _, node := range peers {
err := sendMessage(node, authCode, "election", authCode)
if err != nil {
log.Printf("Error sending election message to %s: %v", node, err)
}
}
isMaster = true
go sendHeartbeats()
}
func handleHeartbeat(content string) {
masterNodeMux.Lock()
defer masterNodeMux.Unlock()
masterNode = content
}
func handleElection(content string) {
masterNodeMux.Lock()
defer masterNodeMux.Unlock()
if content < authCode {
masterNode = content
}
}

View file

@ -11,7 +11,7 @@ func nodeUpdateSync() {
fmt.Println("Syncing updates across all nodes...") fmt.Println("Syncing updates across all nodes...")
for _, peer := range peers { for _, peer := range peers {
fmt.Printf("Notifying node %s about update...\n", peer) fmt.Printf("Notifying node %s about update...\n", peer)
err := sendMessage(peer, connCode, "update", "Start update process") err := sendMessage(peer, authCode, "update", "Start update process")
if err != nil { if err != nil {
log.Printf("Failed to notify node %s: %v\n", peer, err) log.Printf("Failed to notify node %s: %v\n", peer, err)
continue continue

93
node.go
View file

@ -2,18 +2,22 @@ package main
import ( import (
"bytes" "bytes"
"crypto/sha256"
"encoding/hex"
"encoding/json" "encoding/json"
"fmt" "fmt"
"io/ioutil" "io/ioutil"
"log"
"net/http" "net/http"
"sync" "sync"
"time" "time"
) )
var ( var (
connCode string authCode string
peers []string peers []string
connCodeMutex sync.Mutex authMutex sync.Mutex
authenticated = make(map[string]bool)
) )
type Message struct { type Message struct {
@ -22,9 +26,16 @@ type Message struct {
Content string `json:"content"` Content string `json:"content"`
} }
type CrawlerConfig struct {
ID string
Host string
Port int
AuthCode string
}
func loadNodeConfig() { func loadNodeConfig() {
config := loadConfig() config := loadConfig()
connCode = config.ConnectionCode authCode = config.AuthCode // nuh uh
peers = config.Peers peers = config.Peers
} }
@ -34,9 +45,6 @@ func handleNodeRequest(w http.ResponseWriter, r *http.Request) {
return return
} }
connCodeMutex.Lock()
defer connCodeMutex.Unlock()
body, err := ioutil.ReadAll(r.Body) body, err := ioutil.ReadAll(r.Body)
if err != nil { if err != nil {
http.Error(w, "Error reading request body", http.StatusInternalServerError) http.Error(w, "Error reading request body", http.StatusInternalServerError)
@ -50,8 +58,8 @@ func handleNodeRequest(w http.ResponseWriter, r *http.Request) {
return return
} }
if msg.ID != connCode { if !isAuthenticated(msg.ID) {
http.Error(w, "Authentication failed", http.StatusUnauthorized) http.Error(w, "Authentication required", http.StatusUnauthorized)
return return
} }
@ -59,17 +67,55 @@ func handleNodeRequest(w http.ResponseWriter, r *http.Request) {
fmt.Fprintln(w, "Message received") fmt.Fprintln(w, "Message received")
} }
func handleAuth(w http.ResponseWriter, r *http.Request) {
if r.Method != http.MethodPost {
http.Error(w, "Invalid request method", http.StatusMethodNotAllowed)
return
}
body, err := ioutil.ReadAll(r.Body)
if err != nil {
http.Error(w, "Error reading request body", http.StatusInternalServerError)
return
}
defer r.Body.Close()
var authRequest CrawlerConfig
if err := json.Unmarshal(body, &authRequest); err != nil {
http.Error(w, "Error parsing JSON", http.StatusBadRequest)
return
}
expectedCode := GenerateRegistrationCode(authRequest.Host, authRequest.Port, authCode)
if authRequest.AuthCode != expectedCode {
http.Error(w, "Invalid auth code", http.StatusUnauthorized)
return
}
authMutex.Lock()
authenticated[authRequest.ID] = true
authMutex.Unlock()
fmt.Fprintln(w, "Authenticated successfully")
}
func isAuthenticated(id string) bool {
authMutex.Lock()
defer authMutex.Unlock()
return authenticated[id]
}
func interpretMessage(msg Message) { func interpretMessage(msg Message) {
switch msg.Type { switch msg.Type {
case "test": case "test":
fmt.Println("Received test message:", msg.Content) fmt.Println("Received test message:", msg.Content)
case "get-version":
fmt.Println("Received get-version message")
// Handle get-version logic here
case "update": case "update":
fmt.Println("Received update message:", msg.Content) fmt.Println("Received update message:", msg.Content)
// Handle update logic here
go update() go update()
case "heartbeat":
handleHeartbeat(msg.Content)
case "election":
handleElection(msg.Content)
default: default:
fmt.Println("Received unknown message type:", msg.Type) fmt.Println("Received unknown message type:", msg.Type)
} }
@ -111,7 +157,7 @@ func startNodeClient(addresses []string) {
for _, address := range addresses { for _, address := range addresses {
go func(addr string) { go func(addr string) {
for { for {
err := sendMessage(addr, connCode, "test", "This is a test message") err := sendMessage(addr, authCode, "test", "This is a test message")
if err != nil { if err != nil {
fmt.Println("Error sending test message to", addr, ":", err) fmt.Println("Error sending test message to", addr, ":", err)
continue continue
@ -121,3 +167,24 @@ func startNodeClient(addresses []string) {
}(address) }(address)
} }
} }
func GenerateRegistrationCode(host string, port int, authCode string) string {
data := fmt.Sprintf("%s:%d:%s", host, port, authCode)
hash := sha256.Sum256([]byte(data))
return hex.EncodeToString(hash[:])
}
func ParseRegistrationCode(code string, host string, port int, authCode string) (string, int, string, error) {
data := fmt.Sprintf("%s:%d:%s", host, port, authCode)
hash := sha256.Sum256([]byte(data))
expectedCode := hex.EncodeToString(hash[:])
log.Printf("Parsing registration code: %s", code)
log.Printf("Expected registration code: %s", expectedCode)
if expectedCode != code {
return "", 0, "", fmt.Errorf("invalid registration code")
}
return host, port, authCode, nil
}