package main import ( "crypto/tls" _ "embed" "encoding/json" "fmt" "html/template" "log" "net/http" "os" "github.com/dreamscached/minequery/v2" "golang.org/x/crypto/acme/autocert" ) type config struct { Title string `json:"title"` Content template.HTML `json:"content"` Servers []server `json:"servers"` UseTLS bool `json:"useTLS"` TLSHostname string `json:"tlshostname"` ServerPort int `json:"serverport"` BackupDir string `json:"backupDir"` } type server struct { Host string `json:"host"` Port int `json:"port"` Status *minequery.Status17 } type pageData struct { Title string Content template.HTML Servers []server } //go:embed index.html var indexhtml string func main() { // Parse config file configData, err := os.ReadFile("./config.json") if err != nil { log.Fatalf("Error reading config file: %v\n", err) } var config config err = json.Unmarshal(configData, &config) if err != nil { log.Fatalf("Error parsing config file: %v\n", err) } serveBackups := true if stat, err := os.Stat(config.BackupDir); os.IsNotExist(err) || !stat.IsDir() { log.Printf("Backup directory %v does not exist; not serving backups.", config.BackupDir) serveBackups = false } data := pageData{} data.Title = config.Title data.Content = config.Content data.Servers = config.Servers // Set up templates fmt.Print("Parsing templates...\n") t, err := template.New("").Parse(indexhtml) if err != nil { log.Fatalf("Error parsing HTML template: %v\n", err) } http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { if r.URL.Path != "/" { http.NotFound(w, r) return } // Update servers with current data for i, s := range config.Servers { // TODO: Query instead (opportunistically?) to get mod lists, etc config.Servers[i].Status, err = minequery.Ping17(s.Host, s.Port) if err != nil { log.Printf("Error querying server: %v", err) } } err = t.Execute(w, data) if err != nil { log.Printf("Error executing template: %v\n", err) } }) http.HandleFunc("/data.json", func(w http.ResponseWriter, r *http.Request) { // Update servers with current data for i, s := range config.Servers { // TODO: Query instead (opportunistically?) to get mod lists, etc config.Servers[i].Status, err = minequery.Ping17(s.Host, s.Port) if err != nil { log.Printf("Error querying server: %v", err) } } msg, err := json.Marshal(config.Servers) if err != nil { http.Error(w, err.Error(), 500) } w.Header().Set("Content-Type", "application/json") w.Write(msg) }) // Serve backup directory if serveBackups { // TODO: add HTML http.Handle("/backups/", http.StripPrefix("/backups/", http.FileServer(http.Dir(config.BackupDir)))) } if config.UseTLS { certManager := autocert.Manager{ Prompt: autocert.AcceptTOS, HostPolicy: autocert.HostWhitelist(config.TLSHostname), Cache: autocert.DirCache("certs"), } srv := &http.Server{ Addr: ":https", TLSConfig: &tls.Config{ GetCertificate: certManager.GetCertificate, }, } go http.ListenAndServe(":http", certManager.HTTPHandler(nil)) // Handler for LetsEncrypt if config.ServerPort == 0 { // Value not set in JSON config.ServerPort = 443 } fmt.Printf("Serving HTTPS on port %v...\n", config.ServerPort) srv.ListenAndServeTLS("", "") // Key/cert come from srv.TLSConfig } else { if config.ServerPort == 0 { // Value not set in JSON config.ServerPort = 80 } fmt.Printf("Serving HTTP on port %v...\n", config.ServerPort) http.ListenAndServe(fmt.Sprintf(":%v", config.ServerPort), nil) } }