package web import ( "encoding/json" "fmt" "log" "mime" "net/http" "os" "path/filepath" "regexp" "strconv" "text/template" "github.com/tardisx/discord-auto-upload/assets" "github.com/tardisx/discord-auto-upload/config" daulog "github.com/tardisx/discord-auto-upload/log" "github.com/tardisx/discord-auto-upload/uploads" ) // DAUWebServer - stuff for the web server type DAUWebServer struct { ConfigChange chan int } type valueStringResponse struct { Success bool `json:"success"` Value string `json:"value"` } type valueBooleanResponse struct { Success bool `json:"success"` Value bool `json:"value"` } type errorResponse struct { Success bool `json:"success"` Error string `json:"error"` } func getStatic(w http.ResponseWriter, r *http.Request) { // haha this is dumb and I should change it re := regexp.MustCompile(`[^a-zA-Z0-9\.]`) path := r.URL.Path[1:] sanitized_path := re.ReplaceAll([]byte(path), []byte("_")) if string(sanitized_path) == "" { sanitized_path = []byte("index.html") } data, err := assets.Asset(string(sanitized_path)) if err != nil { // Asset was not found. fmt.Fprintln(w, err) } extension := filepath.Ext(string(sanitized_path)) // is this a HTML file? if so wrap it in the template if extension == ".html" { wrapper, _ := assets.Asset("wrapper.tmpl") t := template.Must(template.New("wrapper").Parse(string(wrapper))) var b struct { Body string Path string Version string } b.Body = string(data) b.Path = string(sanitized_path) b.Version = config.CurrentVersion t.Execute(w, b) return } // otherwise we are a static thing w.Header().Set("Content-Type", mime.TypeByExtension(extension)) w.Write(data) // } // TODO there should be locks around all these config accesses func getSetWebhook(w http.ResponseWriter, r *http.Request) { w.Header().Set("Content-Type", "application/json") if r.Method == "GET" { getResponse := valueStringResponse{Success: true, Value: config.Config.WebHookURL} // I can't see any way this will fail js, _ := json.Marshal(getResponse) w.Write(js) } else if r.Method == "POST" { err := r.ParseForm() if err != nil { log.Fatal(err) } config.Config.WebHookURL = r.PostForm.Get("value") config.SaveConfig() postResponse := valueStringResponse{Success: true, Value: config.Config.WebHookURL} js, _ := json.Marshal(postResponse) w.Write(js) } } // TODO there should be locks around all these config accesses func getSetUsername(w http.ResponseWriter, r *http.Request) { w.Header().Set("Content-Type", "application/json") if r.Method == "GET" { getResponse := valueStringResponse{Success: true, Value: config.Config.Username} // I can't see any way this will fail js, _ := json.Marshal(getResponse) w.Write(js) } else if r.Method == "POST" { err := r.ParseForm() if err != nil { log.Fatal(err) } config.Config.Username = r.PostForm.Get("value") config.SaveConfig() postResponse := valueStringResponse{Success: true, Value: config.Config.Username} js, _ := json.Marshal(postResponse) w.Write(js) } } func getSetWatch(w http.ResponseWriter, r *http.Request) { w.Header().Set("Content-Type", "application/json") if r.Method == "GET" { getResponse := valueStringResponse{Success: true, Value: strconv.Itoa(config.Config.Watch)} // I can't see any way this will fail js, _ := json.Marshal(getResponse) w.Write(js) } else if r.Method == "POST" { err := r.ParseForm() if err != nil { log.Fatal(err) } i, err := strconv.Atoi(r.PostForm.Get("value")) if err != nil { response := errorResponse{Success: false, Error: fmt.Sprintf("Bad value for watch: %v", err)} js, _ := json.Marshal(response) w.Write(js) return } if i < 1 { response := errorResponse{Success: false, Error: "must be > 0"} js, _ := json.Marshal(response) w.Write(js) return } config.Config.Watch = i config.SaveConfig() postResponse := valueStringResponse{Success: true, Value: strconv.Itoa(config.Config.Watch)} js, _ := json.Marshal(postResponse) w.Write(js) } } func getSetNoWatermark(w http.ResponseWriter, r *http.Request) { w.Header().Set("Content-Type", "application/json") if r.Method == "GET" { getResponse := valueBooleanResponse{Success: true, Value: config.Config.NoWatermark} // I can't see any way this will fail js, _ := json.Marshal(getResponse) w.Write(js) } else if r.Method == "POST" { err := r.ParseForm() if err != nil { log.Fatal(err) } v := r.PostForm.Get("value") if v != "0" && v != "1" { response := errorResponse{Success: false, Error: fmt.Sprintf("Bad value for nowatermark: %v", err)} js, _ := json.Marshal(response) w.Write(js) return } if v == "0" { config.Config.NoWatermark = false } else { config.Config.NoWatermark = true } config.SaveConfig() postResponse := valueBooleanResponse{Success: true, Value: config.Config.NoWatermark} js, _ := json.Marshal(postResponse) w.Write(js) } } func getSetDirectory(w http.ResponseWriter, r *http.Request) { w.Header().Set("Content-Type", "application/json") if r.Method == "GET" { getResponse := valueStringResponse{Success: true, Value: config.Config.Path} // I can't see any way this will fail js, _ := json.Marshal(getResponse) w.Write(js) } else if r.Method == "POST" { err := r.ParseForm() if err != nil { log.Fatal(err) } newPath := r.PostForm.Get("value") // sanity check this path stat, err := os.Stat(newPath) if os.IsNotExist(err) { // not exist response := errorResponse{Success: false, Error: fmt.Sprintf("Path: %s - does not exist", newPath)} js, _ := json.Marshal(response) w.Write(js) return } else if !stat.IsDir() { // not a directory response := errorResponse{Success: false, Error: fmt.Sprintf("Path: %s - is not a directory", newPath)} js, _ := json.Marshal(response) w.Write(js) return } config.Config.Path = newPath config.SaveConfig() postResponse := valueStringResponse{Success: true, Value: config.Config.Path} js, _ := json.Marshal(postResponse) w.Write(js) } } func getSetExclude(w http.ResponseWriter, r *http.Request) { w.Header().Set("Content-Type", "application/json") if r.Method == "GET" { getResponse := valueStringResponse{Success: true, Value: config.Config.Exclude} // I can't see any way this will fail js, _ := json.Marshal(getResponse) w.Write(js) } else if r.Method == "POST" { err := r.ParseForm() if err != nil { log.Fatal(err) } config.Config.Exclude = r.PostForm.Get("value") config.SaveConfig() postResponse := valueStringResponse{Success: true, Value: config.Config.Exclude} js, _ := json.Marshal(postResponse) w.Write(js) } } func getLogs(w http.ResponseWriter, r *http.Request) { w.Header().Set("Content-Type", "text/plain") showDebug := false debug, present := r.URL.Query()["debug"] if present && len(debug[0]) > 0 && debug[0] != "0" { showDebug = true } text := "" for _, log := range daulog.LogEntries { if !showDebug && log.Type == daulog.LogTypeDebug { continue } text = text + fmt.Sprintf( "%-6s %-19s %s\n", log.Type, log.Timestamp.Format("2006-01-02 15:04:05"), log.Entry, ) } // js, _ := json.Marshal(daulog.LogEntries) w.Write([]byte(text)) } func getUploads(w http.ResponseWriter, r *http.Request) { w.Header().Set("Content-Type", "application/json") ups := uploads.Uploads text, _ := json.Marshal(ups) w.Write([]byte(text)) } func StartWebServer() { http.HandleFunc("/", getStatic) http.HandleFunc("/rest/config/webhook", getSetWebhook) http.HandleFunc("/rest/config/username", getSetUsername) http.HandleFunc("/rest/config/watch", getSetWatch) http.HandleFunc("/rest/config/nowatermark", getSetNoWatermark) http.HandleFunc("/rest/config/directory", getSetDirectory) http.HandleFunc("/rest/config/exclude", getSetExclude) http.HandleFunc("/rest/logs", getLogs) http.HandleFunc("/rest/uploads", getUploads) go func() { log.Print("Starting web server on http://localhost:9090") err := http.ListenAndServe(":9090", nil) // set listen port if err != nil { log.Fatal("ListenAndServe: ", err) } }() }