package web import ( "bytes" "fmt" "regexp" "log" "io/ioutil" "net/http" "path/filepath" "xengineering.eu/ceres/utils" "github.com/yuin/goldmark" ) const ( VALID_ID_REGEX = `^[0-9]+$` ) func static(filename string, staticRoot string) func(http.ResponseWriter, *http.Request) { return func(w http.ResponseWriter, r *http.Request) { path := filepath.Join(staticRoot, filename) log.Printf("Trying to serve: %s\n", path) http.ServeFile(w, r, path) } } func index(db *utils.Database, templateRoot string) func(http.ResponseWriter, *http.Request) { return func(w http.ResponseWriter, r *http.Request) { // get data from database cmd := "SELECT id,title FROM recipes ORDER BY title;" log.Printf("Query: %s", cmd) rows, err := db.Backend.Query(cmd) if err != nil { utils.Err(w, 1) return } defer rows.Close() // prepare data store type Element struct { Id string Title string } elements := make([]Element, 0) // scan database rows to data store for rows.Next() { var element Element err := rows.Scan(&element.Id, &element.Title) if err != nil { utils.Err(w, 2) return } else { elements = append(elements, element) } } // render and return template path := filepath.Join(templateRoot, "index.html") utils.ServeTemplate(w, "index", path, elements) } } func recipe(db *utils.Database, templateRoot string) func(http.ResponseWriter, *http.Request) { return func(w http.ResponseWriter, r *http.Request) { // get id from URL parameters ids := r.URL.Query()["id"] if len(ids) != 1 { utils.Err(w, 3, len(ids)) return } idStr := ids[0] // validate id idRegex := regexp.MustCompile(VALID_ID_REGEX) if !(idRegex.MatchString(idStr)) { utils.Err(w, 4, idStr, VALID_ID_REGEX) return } if r.Method == "GET" { // get data from database cmd := fmt.Sprintf("SELECT title,upstream_url,description_markdown FROM recipes WHERE (id='%s');", idStr) log.Printf("Query: %s", cmd) rows, err := db.Backend.Query(cmd) if err != nil { utils.Err(w, 5, err) return } defer rows.Close() // prepare data store type Element struct { Id string Title string UpstreamUrl string DescriptionMarkdown string RenderedDescriptionMarkdown string } elements := make([]Element, 0) // scan database rows to data store for rows.Next() { var element Element element.Id = idStr err := rows.Scan(&element.Title, &element.UpstreamUrl, &element.DescriptionMarkdown) if err != nil { utils.Err(w, 2) return } else { elements = append(elements, element) } } // check result if len(elements) != 1 { utils.Err(w, 6, len(elements)) return } // render markdown var buf bytes.Buffer goldmark.Convert([]byte(elements[0].DescriptionMarkdown), &buf) elements[0].RenderedDescriptionMarkdown = buf.String() // render and return template path := filepath.Join(templateRoot, "recipe.html") utils.ServeTemplate(w, "recipe", path, elements[0]) } if r.Method == "POST" { // read request body buffer,_ := ioutil.ReadAll(r.Body) // FIXME error handling body := string(buffer) updateRecipe(db, body, idStr) } } } func recipe_edit(db *utils.Database, templateRoot string) func(http.ResponseWriter, *http.Request) { return func(w http.ResponseWriter, r *http.Request) { // get id from URL parameters ids := r.URL.Query()["id"] if len(ids) != 1 { utils.Err(w, 3, len(ids)) return } idStr := ids[0] // validate id idRegex := regexp.MustCompile(VALID_ID_REGEX) if !(idRegex.MatchString(idStr)) { utils.Err(w, 4, idStr, VALID_ID_REGEX) return } if r.Method == "GET" { // get data from database cmd := fmt.Sprintf("SELECT title,upstream_url,description_markdown FROM recipes WHERE (id='%s');", idStr) log.Printf("Query: %s", cmd) rows, err := db.Backend.Query(cmd) if err != nil { utils.Err(w, 5, err) return } defer rows.Close() // prepare data store type Element struct { Id string Title string UpstreamUrl string DescriptionMarkdown string RenderedDescriptionMarkdown string } elements := make([]Element, 0) // scan database rows to data store for rows.Next() { var element Element element.Id = idStr err := rows.Scan(&element.Title, &element.UpstreamUrl, &element.DescriptionMarkdown) if err != nil { utils.Err(w, 2) return } else { elements = append(elements, element) } } // check result if len(elements) != 1 { utils.Err(w, 6, len(elements)) return } // render markdown // var buf bytes.Buffer // goldmark.Convert([]byte(elements[0].DescriptionMarkdown), &buf) // elements[0].RenderedDescriptionMarkdown = buf.String() // render and return template path := filepath.Join(templateRoot, "recipe_edit.html") utils.ServeTemplate(w, "recipe", path, elements[0]) } if r.Method == "POST" { // read request body buffer,_ := ioutil.ReadAll(r.Body) // FIXME error handling body := string(buffer) updateRecipe(db, body, idStr) } } } func updateRecipe(db *utils.Database, body string, idStr string) { // execute SQL UPDATE _,_ = db.Backend.Exec(` UPDATE recipes SET description_markdown=? WHERE (id=?); `, body, idStr, ) // FIXME error handling return } func image(storageRoot string) func(http.ResponseWriter, *http.Request) { return func(w http.ResponseWriter, r *http.Request) { // get ID ids := r.URL.Query()["id"] if len(ids) != 1 { utils.Err(w, 3, len(ids)) return } idStr := ids[0] // validate ID idRegex := regexp.MustCompile(VALID_ID_REGEX) if !idRegex.MatchString(idStr) { utils.Err(w, 4, idStr, VALID_ID_REGEX) return } // serve image path := fmt.Sprintf("recipes/image/%s.jpg", idStr) utils.ServeStorage(w, r, storageRoot, path) } } func add_recipes(db *utils.Database, storageRoot string, staticRoot string) func(http.ResponseWriter, *http.Request) { return func(w http.ResponseWriter, r *http.Request) { if r.Method == "GET" { filename := "add.html" path := filepath.Join(staticRoot, filename) log.Printf("Trying to serve: %s", path) http.ServeFile(w, r, path) return } if r.Method == "POST" { url := r.FormValue("url") title := r.FormValue("title") cmd := fmt.Sprintf("INSERT INTO recipes (title,upstream_url) VALUES ('%s', '%s')", title, url) log.Println(cmd) res,err := db.Backend.Exec(cmd) if err != nil { utils.Err(w, 9, err) return } id,err := res.LastInsertId() if err != nil { utils.Err(w, 11, err) return } else { log.Println("Added custom recipe.") redirect := fmt.Sprintf("/recipe?id=%d", id) http.Redirect(w, r, redirect, 303) return } } } }