summaryrefslogtreecommitdiff
path: root/handler.go
diff options
context:
space:
mode:
authorxengineering <me@xengineering.eu>2023-04-01 21:18:29 +0200
committerxengineering <me@xengineering.eu>2023-04-02 21:52:10 +0200
commitbae6e34c9119750b2cba2a2ce6d88e9f8c895900 (patch)
treec171db86da17301c1fec735e1f10f4e9d91f1b41 /handler.go
parent996725cb4735e1026bb3409d52b5a61bc8274fd2 (diff)
downloadceres-bae6e34c9119750b2cba2a2ce6d88e9f8c895900.tar
ceres-bae6e34c9119750b2cba2a2ce6d88e9f8c895900.tar.zst
ceres-bae6e34c9119750b2cba2a2ce6d88e9f8c895900.zip
Switch from MariaDB to files
Using a database is way more complex (see the commit statistics of this commit) than using files to store recipe data. Also administration and usage is simpler.
Diffstat (limited to 'handler.go')
-rw-r--r--handler.go233
1 files changed, 100 insertions, 133 deletions
diff --git a/handler.go b/handler.go
index 46e1b02..adfcd61 100644
--- a/handler.go
+++ b/handler.go
@@ -2,6 +2,7 @@ package main
import (
"bufio"
+ "os"
"bytes"
"fmt"
"io/ioutil"
@@ -9,6 +10,7 @@ import (
"path/filepath"
"regexp"
"strings"
+ "strconv"
"github.com/yuin/goldmark"
)
@@ -17,6 +19,13 @@ const (
VALID_ID_REGEX = `^[0-9]+$`
)
+type Recipe struct {
+ Id string
+ Title string
+ Text string
+ Html string
+}
+
func titleFromMd(md string) string {
scanner := bufio.NewScanner(strings.NewReader(md))
for scanner.Scan() {
@@ -31,34 +40,36 @@ func titleFromMd(md string) string {
func indexGet(w http.ResponseWriter, r *http.Request) {
- cmd := "SELECT id,description_markdown FROM recipes ORDER BY id;"
- rows, err := db.Query(cmd)
+ entries, err := os.ReadDir("data/storage/recipes")
if err != nil {
- http.Error(w, "Failed to load recipes from database.", 500)
+ http.Error(w, "Could not list recipes!", 500)
return
}
- defer rows.Close()
- type Element struct {
- Id string
- Title string
- DescriptionMarkdown string
- }
- elements := make([]Element, 0)
+ recipes := make([]Recipe, 0)
+
+ for _,v := range entries {
+ if v.IsDir() == false {
+ continue
+ }
- for rows.Next() {
- var element Element
- err := rows.Scan(&element.Id, &element.DescriptionMarkdown)
+ _, err = strconv.Atoi(v.Name())
if err != nil {
- http.Error(w, "Could not parse recipe from database request.", 500)
- return
- } else {
- element.Title = titleFromMd(element.DescriptionMarkdown)
- elements = append(elements, element)
+ continue
}
+
+ textpath := fmt.Sprintf("data/storage/recipes/%s/text", v.Name())
+ data, _ := ioutil.ReadFile(textpath)
+
+ recipes = append(recipes, Recipe{
+ v.Name(),
+ titleFromMd(string(data)),
+ string(data),
+ "",
+ })
}
- ServeTemplate(w, "index.html", elements)
+ ServeTemplate(w, "index.html", recipes)
}
func recipeGet(w http.ResponseWriter, r *http.Request) {
@@ -71,104 +82,47 @@ func recipeGet(w http.ResponseWriter, r *http.Request) {
}
idStr := ids[0]
- idRegex := regexp.MustCompile(VALID_ID_REGEX)
- if !(idRegex.MatchString(idStr)) {
- http.Error(w, "Bad 'id' URL parameter.", 400)
- return
- }
-
- cmd := fmt.Sprintf("SELECT description_markdown FROM recipes WHERE (id='%s');", idStr)
- rows, err := db.Query(cmd)
- if err != nil {
- http.Error(w, "Database returned error: "+err.Error(), 500)
- return
- }
- defer rows.Close()
-
- type Element struct {
- Id string
- Title string
- DescriptionMarkdown string
- RenderedDescriptionMarkdown string
- }
- elements := make([]Element, 0)
+ textpath := fmt.Sprintf("data/storage/recipes/%s/text", idStr)
+ data, _ := ioutil.ReadFile(textpath)
- for rows.Next() {
- var element Element
- element.Id = idStr
- err := rows.Scan(&element.DescriptionMarkdown)
- if err != nil {
- http.Error(w, "Could not parse recipe from database request.", 500)
- return
- } else {
- element.Title = titleFromMd(element.DescriptionMarkdown)
- elements = append(elements, element)
- }
- }
-
- if len(elements) != 1 {
- http.Error(w, "Expected exactly 1 recipe from database.", 500)
- return
+ recipe := Recipe{
+ idStr,
+ titleFromMd(string(data)),
+ string(data),
+ "",
}
titleRegex := regexp.MustCompile(`\# .*`)
- elements[0].DescriptionMarkdown = titleRegex.ReplaceAllString(elements[0].DescriptionMarkdown, "")
+ recipe.Text = titleRegex.ReplaceAllString(recipe.Text, "")
- // render markdown
var buf bytes.Buffer
- goldmark.Convert([]byte(elements[0].DescriptionMarkdown), &buf)
- elements[0].RenderedDescriptionMarkdown = buf.String()
+ goldmark.Convert([]byte(recipe.Text), &buf)
+ recipe.Html = buf.String()
- ServeTemplate(w, "recipe.html", elements[0])
+ ServeTemplate(w, "recipe.html", recipe)
}
func recipeEditGet(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)
+ msg := fmt.Sprintf("Exactly 1 'id' URL parameter expected but %d provided.", len(ids))
+ http.Error(w, msg, 400)
return
}
idStr := ids[0]
- idRegex := regexp.MustCompile(VALID_ID_REGEX)
- if !(idRegex.MatchString(idStr)) {
- http.Error(w, "Bad 'id' URL parameter.", 400)
- return
- }
-
- cmd := fmt.Sprintf("SELECT description_markdown FROM recipes WHERE (id='%s');", idStr)
- rows, err := db.Query(cmd)
- if err != nil {
- http.Error(w, "Got error from database: "+err.Error(), 500)
- return
- }
- defer rows.Close()
-
- type Element struct {
- Id string
- DescriptionMarkdown string
- }
- elements := make([]Element, 0)
+ textpath := fmt.Sprintf("data/storage/recipes/%s/text", idStr)
+ data, _ := ioutil.ReadFile(textpath)
- for rows.Next() {
- var element Element
- element.Id = idStr
- err := rows.Scan(&element.DescriptionMarkdown)
- if err != nil {
- http.Error(w, "Could not parse recipe from database request.", 500)
- return
- } else {
- elements = append(elements, element)
- }
+ recipe := Recipe{
+ idStr,
+ "",
+ string(data),
+ "",
}
- if len(elements) != 1 {
- http.Error(w, "Did not get exactly one recipe from database.", 500)
- return
- }
-
- ServeTemplate(w, "recipe_edit.html", elements[0])
+ ServeTemplate(w, "recipe_edit.html", recipe)
}
func recipeEditPost(w http.ResponseWriter, r *http.Request) {
@@ -186,9 +140,17 @@ func recipeEditPost(w http.ResponseWriter, r *http.Request) {
return
}
- buffer, _ := ioutil.ReadAll(r.Body) // FIXME error handling
- body := string(buffer)
- updateRecipe(body, idStr)
+ buffer, err := ioutil.ReadAll(r.Body)
+ if err != nil {
+ http.Error(w, "Could not read request body.", 400)
+ return
+ }
+
+ textpath := fmt.Sprintf("data/storage/recipes/%s/text", idStr)
+ 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) {
@@ -198,15 +160,10 @@ func recipeConfirmDeletionGet(w http.ResponseWriter, r *http.Request) {
http.Error(w, "Exactly 1 'id' URL parameter expected.", 400)
return
}
- idStr := ids[0]
- type Element struct {
- Id string
- }
- var element Element
- element.Id = idStr
+ recipe := Recipe{ids[0], "", "", ""}
- ServeTemplate(w, "recipe_confirm_deletion.html", element)
+ ServeTemplate(w, "recipe_confirm_deletion.html", recipe)
}
func recipeConfirmDeletionPost(w http.ResponseWriter, r *http.Request) {
@@ -216,12 +173,10 @@ func recipeConfirmDeletionPost(w http.ResponseWriter, r *http.Request) {
http.Error(w, "Exactly 1 'id' URL parameter expected.", 400)
return
}
- idStr := ids[0]
- cmd := fmt.Sprintf("DELETE FROM recipes where (id='%s');", idStr)
- _, err := db.Query(cmd)
+ recipedir := fmt.Sprintf("data/storage/recipes/%s", ids[0])
+ err := os.RemoveAll(recipedir)
if err != nil {
- fmt.Print(err)
http.Error(w, "Could not delete recipe.", 500)
return;
}
@@ -229,37 +184,49 @@ func recipeConfirmDeletionPost(w http.ResponseWriter, r *http.Request) {
http.Redirect(w, r, "/index.html", 303)
}
-func updateRecipe(body string, idStr string) {
+func addRecipesGet(w http.ResponseWriter, r *http.Request) {
- _, _ = db.Exec(`
- UPDATE
- recipes
- SET
- description_markdown=?
- WHERE
- (id=?);
- `,
- body, idStr,
- ) // FIXME error handling
+ entries, err := os.ReadDir("data/storage/recipes")
+ if err != nil {
+ http.Error(w, "Could not get list of existing recipes!", 500)
+ return
+ }
- return
-}
+ var biggest int = -1
-func addRecipesGet(w http.ResponseWriter, r *http.Request) {
+ for _,v := range entries {
+ if v.IsDir() == false {
+ continue
+ }
- cmd := fmt.Sprintf("INSERT INTO recipes (title) VALUES ('%s')", "New recipe")
- res, err := db.Exec(cmd)
+ number, err := strconv.Atoi(v.Name())
+ if err != nil {
+ continue
+ }
+
+ if number > biggest {
+ biggest = number
+ }
+ }
+
+ newId := biggest + 1
+
+ recipedir := fmt.Sprintf("data/storage/recipes/%d", newId)
+ err = os.Mkdir(recipedir, 0755)
if err != nil {
- http.Error(w, "Could not create recipe.", 500)
+ http.Error(w, "Could not create new recipe!", 500)
return
}
- id, err := res.LastInsertId()
+
+ textpath := fmt.Sprintf("data/storage/recipes/%d/text", newId)
+ err = os.WriteFile(textpath, make([]byte, 0), 0644)
if err != nil {
- http.Error(w, "Could not get new recipe ID from database", 500)
- } else {
- redirect := fmt.Sprintf("/recipe/edit?id=%d", id)
- http.Redirect(w, r, redirect, 303)
+ 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) {