wiki.go 2.2 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697
  1. package main
  2. import (
  3. "net/http"
  4. "io/ioutil"
  5. "log"
  6. "regexp"
  7. "text/template"
  8. )
  9. const lenPath = len("/view/")
  10. var titleValidator = regexp.MustCompile("^[a-zA-Z0-9]+$")
  11. var templates = make(map[string]*template.Template)
  12. var err error
  13. type Page struct {
  14. Title string
  15. Body []byte
  16. }
  17. func init() {
  18. for _, tmpl := range []string{"edit", "view"} {
  19. templates[tmpl] = template.Must(template.ParseFiles(tmpl + ".html"))
  20. }
  21. }
  22. func main() {
  23. http.HandleFunc("/view/", makeHandler(viewHandler))
  24. http.HandleFunc("/edit/", makeHandler(editHandler))
  25. http.HandleFunc("/save/", makeHandler(saveHandler))
  26. err := http.ListenAndServe("localhost:8080", nil)
  27. if err != nil {
  28. log.Fatal("ListenAndServe: ", err.Error())
  29. }
  30. }
  31. func makeHandler(fn func(http.ResponseWriter, *http.Request, string)) http.HandlerFunc {
  32. return func(w http.ResponseWriter, r *http.Request) {
  33. title := r.URL.Path[lenPath:]
  34. if !titleValidator.MatchString(title) {
  35. http.NotFound(w, r)
  36. return
  37. }
  38. fn(w, r, title)
  39. }
  40. }
  41. func viewHandler(w http.ResponseWriter, r *http.Request, title string) {
  42. p, err := load(title)
  43. if err != nil { // page not found
  44. http.Redirect(w, r, "/edit/"+title, http.StatusFound)
  45. return
  46. }
  47. renderTemplate(w, "view", p)
  48. }
  49. func editHandler(w http.ResponseWriter, r *http.Request, title string) {
  50. p, err := load(title)
  51. if err != nil {
  52. p = &Page{Title: title}
  53. }
  54. renderTemplate(w, "edit", p)
  55. }
  56. func saveHandler(w http.ResponseWriter, r *http.Request, title string) {
  57. body := r.FormValue("body")
  58. p := &Page{Title: title, Body: []byte(body)}
  59. err := p.save()
  60. if err != nil {
  61. http.Error(w, err.Error(), http.StatusInternalServerError)
  62. return
  63. }
  64. http.Redirect(w, r, "/view/"+title, http.StatusFound)
  65. }
  66. func renderTemplate(w http.ResponseWriter, tmpl string, p *Page) {
  67. err := templates[tmpl].Execute(w, p)
  68. if err != nil {
  69. http.Error(w, err.Error(), http.StatusInternalServerError)
  70. }
  71. }
  72. func (p *Page) save() error {
  73. filename := p.Title + ".txt"
  74. // file created with read-write permissions for the current user only
  75. return ioutil.WriteFile(filename, p.Body, 0600)
  76. }
  77. func load(title string) (*Page, error) {
  78. filename := title + ".txt"
  79. body, err := ioutil.ReadFile(filename)
  80. if err != nil {
  81. return nil, err
  82. }
  83. return &Page{Title: title, Body: body}, nil
  84. }