handlers.go 4.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204
  1. package api
  2. import (
  3. "encoding/json"
  4. "fmt"
  5. "io/ioutil"
  6. "log"
  7. "net/http"
  8. "path/filepath"
  9. "runtime/debug"
  10. "gogs.carducci-dante.gov.it/karmen/core/orm"
  11. "gogs.carducci-dante.gov.it/karmen/core/renderer"
  12. jwtmiddleware "github.com/auth0/go-jwt-middleware"
  13. jwt "github.com/dgrijalva/jwt-go"
  14. "github.com/gorilla/mux"
  15. "github.com/gorilla/sessions"
  16. )
  17. type User struct {
  18. Name string
  19. Admin bool
  20. }
  21. type PathPattern struct {
  22. Path string
  23. RedirectPath string
  24. Methods []string
  25. }
  26. var (
  27. signingKey = []byte("secret")
  28. store = sessions.NewCookieStore([]byte("something-very-secret"))
  29. jwtMiddleware = jwtmiddleware.New(jwtmiddleware.Options{
  30. ValidationKeyGetter: func(token *jwt.Token) (interface{}, error) {
  31. return signingKey, nil
  32. },
  33. SigningMethod: jwt.SigningMethodHS256,
  34. Extractor: fromCookie,
  35. ErrorHandler: onError,
  36. })
  37. patterns []PathPattern = []PathPattern{
  38. PathPattern{"/%s", "", []string{"GET"}},
  39. PathPattern{"/%s/{id}", "", []string{"GET"}},
  40. PathPattern{"/%s/add/", "/%s/%d?format=html&tpl_layout=base&tpl_content=%s_show", []string{"GET", "POST"}},
  41. PathPattern{"/%s/{id}/update", "/%s/%d?format=html&tpl_layout=base&tpl_content=%s_show", []string{"GET", "POST"}},
  42. PathPattern{"/%s/{id}/delete", "/%s?format=html&tpl_layout=base&tpl_content=%s", []string{"DELETE"}},
  43. }
  44. )
  45. // Generate CRUD handlers
  46. func generateHandler(r *mux.Router, base string) {
  47. for _, pattern := range patterns {
  48. r.Handle(fmt.Sprintf(pattern.Path, base), jwtMiddleware.Handler(recoverHandler(modelHandler(base, fmt.Sprintf(pattern.Path, base), pattern.RedirectPath)))).Methods(pattern.Methods...)
  49. }
  50. }
  51. func Handlers() *mux.Router {
  52. r := mux.NewRouter()
  53. // Authentication
  54. r.Handle("/login", loginHandler())
  55. r.Handle("/logout", logoutHandler())
  56. // Dashboard
  57. r.Handle("/", jwtMiddleware.Handler(recoverHandler(dashboardHandler())))
  58. // Generate model handlers
  59. for _, model := range []string{"teachers", "classes", "subjects", "activities"} {
  60. generateHandler(r, model)
  61. }
  62. // Static file server
  63. r.PathPrefix("/").Handler(http.FileServer(http.Dir("./dist/")))
  64. return r
  65. }
  66. func onError(w http.ResponseWriter, r *http.Request, err string) {
  67. http.Redirect(w, r, "/login", http.StatusTemporaryRedirect)
  68. }
  69. func respondWithStaticFile(w http.ResponseWriter, filename string) error {
  70. f, err := ioutil.ReadFile(filepath.Join("public/html", filename))
  71. if err != nil {
  72. return err
  73. }
  74. w.Write(f)
  75. return nil
  76. }
  77. func fromCookie(r *http.Request) (string, error) {
  78. session, err := store.Get(r, "login-session")
  79. if err != nil {
  80. return "", nil
  81. }
  82. if session.Values["token"] == nil {
  83. return "", nil
  84. }
  85. token := session.Values["token"].([]uint8)
  86. return string(token), nil
  87. }
  88. func recoverHandler(next http.Handler) http.Handler {
  89. fn := func(w http.ResponseWriter, r *http.Request) {
  90. defer func() {
  91. if err := recover(); err != nil {
  92. panicMsg := fmt.Sprintf("PANIC: %v\n\n== STACKTRACE ==\n%s", err, debug.Stack())
  93. log.Print(panicMsg)
  94. http.Error(w, panicMsg, http.StatusInternalServerError)
  95. }
  96. }()
  97. next.ServeHTTP(w, r)
  98. }
  99. return http.HandlerFunc(fn)
  100. }
  101. func modelHandler(base, path, redirectPath string) http.Handler {
  102. fn := func(w http.ResponseWriter, r *http.Request) {
  103. var (
  104. ok bool
  105. getFn orm.GetFn
  106. postFn orm.PostFn
  107. )
  108. switch r.Method {
  109. case "GET":
  110. getFn, ok = orm.Get[path]
  111. if !ok {
  112. renderer.Render[r.URL.Query()["format"][0]](w, r, fmt.Errorf("Can't find ORM function for path %s!", path))
  113. }
  114. data, err := getFn(mux.Vars(r))
  115. if err != nil {
  116. renderer.Render[r.URL.Query()["format"][0]](w, r, err)
  117. } else {
  118. renderer.Render[r.URL.Query()["format"][0]](w, r, data, r.URL.Query())
  119. }
  120. case "POST":
  121. postFn, ok = orm.Post[path]
  122. if !ok {
  123. renderer.Render[r.URL.Query()["format"][0]](w, r, fmt.Errorf("Can't find ORM function for path %s!", path))
  124. }
  125. data, err := postFn(mux.Vars(r), r)
  126. if err != nil {
  127. renderer.Render["html"](w, r, err)
  128. } else {
  129. if mux.Vars(r)["id"] != "" {
  130. http.Redirect(w, r,
  131. fmt.Sprintf(
  132. "/%s/%s?format=html&tpl_layout=base&tpl_content=%s_show",
  133. base,
  134. mux.Vars(r)["id"],
  135. base,
  136. ),
  137. http.StatusSeeOther,
  138. )
  139. } else {
  140. http.Redirect(w, r,
  141. fmt.Sprintf(
  142. "/%s/%d?format=html&tpl_layout=base&tpl_content=%s_show",
  143. base,
  144. data.GetID(),
  145. base,
  146. ),
  147. http.StatusSeeOther,
  148. )
  149. }
  150. }
  151. case "DELETE":
  152. postFn, ok = orm.Post[path]
  153. if !ok {
  154. renderer.Render[r.URL.Query().Get("format")](w, r, fmt.Errorf("Can't find ORM function for path %s!", path))
  155. }
  156. _, err := postFn(mux.Vars(r), r)
  157. if err != nil {
  158. renderer.Render["html"](w, r, err)
  159. } else {
  160. var data struct {
  161. RedirectUrl string `json:"redirect_url"`
  162. }
  163. data.RedirectUrl = fmt.Sprintf(redirectPath, base, base)
  164. w.Header().Set("Content-Type", "application/json")
  165. json.NewEncoder(w).Encode(data)
  166. }
  167. }
  168. }
  169. return http.HandlerFunc(fn)
  170. }