package main import ( "fmt" "io/ioutil" "net/http" "os" "path/filepath" "regexp" "sort" "strconv" ) const ( VALID_ID_REGEX = `^[0-9]+$` ) func indexGet(w http.ResponseWriter, r *http.Request) { list := getRecipeList() sort.Sort(list) ServeTemplate(w, "index.html", list) } func recipeGet(w http.ResponseWriter, r *http.Request) { ids := r.URL.Query()["id"] if len(ids) != 1 { msg := fmt.Sprintf("Exactly 1 'id' URL parameter expected but %d provided.", len(ids)) http.Error(w, msg, 400) return } idStr := ids[0] rec, err := getRecipe(idStr) if err != nil { http.Error(w, "Could not get recipe.", 400) return } data := struct{ Id string Recipe recipe }{idStr, rec} ServeTemplate(w, "recipe.html", data) } func recipeEditGet(w http.ResponseWriter, r *http.Request) { ids := r.URL.Query()["id"] if len(ids) != 1 { msg := fmt.Sprintf("Exactly 1 'id' URL parameter expected but %d provided.", len(ids)) http.Error(w, msg, 400) return } idStr := ids[0] text, err := getRecipeText(idStr) if err != nil { http.Error(w, "Could not get recipe.", 400) return } rec, err := getRecipe(idStr) if err != nil { http.Error(w, "Could not get recipe.", 400) return } recipe := struct{ Id string Title string Text string }{idStr, rec.Title, string(text)} ServeTemplate(w, "recipe_edit.html", recipe) } func recipeEditPost(w http.ResponseWriter, r *http.Request) { ids := r.URL.Query()["id"] if len(ids) != 1 { http.Error(w, "Exactly 1 'id' URL parameter expected.", 400) return } idStr := ids[0] idRegex := regexp.MustCompile(VALID_ID_REGEX) if !(idRegex.MatchString(idStr)) { http.Error(w, "Bad 'id' URL parameter.", 400) return } buffer, err := ioutil.ReadAll(r.Body) if err != nil { http.Error(w, "Could not read request body.", 400) return } textpath := filepath.Join(config.Data, "recipes", idStr, "text") err = ioutil.WriteFile(textpath, buffer, 0644) if err != nil { http.Error(w, "Could not save new text for recipe.", 500) } } func recipeConfirmDeletionGet(w http.ResponseWriter, r *http.Request) { ids := r.URL.Query()["id"] if len(ids) != 1 { http.Error(w, "Exactly 1 'id' URL parameter expected.", 400) return } ServeTemplate(w, "recipe_confirm_deletion.html", struct{Id string}{ids[0]}) } func recipeConfirmDeletionPost(w http.ResponseWriter, r *http.Request) { ids := r.URL.Query()["id"] if len(ids) != 1 { http.Error(w, "Exactly 1 'id' URL parameter expected.", 400) return } recipedir := filepath.Join(config.Data, "recipes", ids[0]) err := os.RemoveAll(recipedir) if err != nil { http.Error(w, "Could not delete recipe.", 500) return } http.Redirect(w, r, "/index.html", 303) } func addRecipesGet(w http.ResponseWriter, r *http.Request) { var biggest int = -1 entries, _ := os.ReadDir(filepath.Join(config.Data, "recipes")) for _, v := range entries { if v.IsDir() == false { continue } number, err := strconv.Atoi(v.Name()) if err != nil { continue } if number > biggest { biggest = number } } newId := biggest + 1 newIdStr := strconv.Itoa(newId) recipedir := filepath.Join(config.Data, "recipes", newIdStr) err := os.Mkdir(recipedir, 0755) if err != nil { http.Error(w, "Could not create new recipe!", 500) return } textpath := filepath.Join(config.Data, "recipes", newIdStr, "text") err = os.WriteFile(textpath, make([]byte, 0), 0644) if err != nil { http.Error(w, "Could not create new recipe!", 500) return } redirect := fmt.Sprintf("/recipe/edit?id=%d", newId) http.Redirect(w, r, redirect, 303) } func staticGet(w http.ResponseWriter, r *http.Request, filename string) { path := filepath.Join(config.Static, filename) http.ServeFile(w, r, path) }