handlers.go 5.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225
  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. jsonPatterns []PathPattern = []PathPattern{
  45. PathPattern{"/api/%s", "", []string{"GET"}},
  46. PathPattern{"/api/%s/{id}", "", []string{"GET"}},
  47. PathPattern{"/api/%s/add/", "/%s/%d?format=json&tpl_layout=base&tpl_content=%s_show", []string{"GET", "POST"}},
  48. PathPattern{"/api/%s/{id}/update", "/%s/%d?format=json&tpl_layout=base&tpl_content=%s_show", []string{"GET", "POST"}},
  49. PathPattern{"/api/%s/{id}/delete", "/%s?format=json&tpl_layout=base&tpl_content=%s", []string{"DELETE"}},
  50. }
  51. )
  52. // Generate CRUD handlers
  53. func generateHandler(r *mux.Router, base string) {
  54. for _, pattern := range patterns {
  55. r.Handle(fmt.Sprintf(pattern.Path, base), jwtMiddleware.Handler(recoverHandler(modelHandler(base, fmt.Sprintf(pattern.Path, base), pattern.RedirectPath)))).Methods(pattern.Methods...)
  56. }
  57. }
  58. func generateAPIHandler(r *mux.Router, base string) {
  59. for _, pattern := range jsonPatterns {
  60. r.Handle(fmt.Sprintf(pattern.Path, base), recoverHandler(modelHandler(base, fmt.Sprintf(pattern.Path, base), pattern.RedirectPath))).Methods(pattern.Methods...)
  61. }
  62. }
  63. func Handlers() *mux.Router {
  64. r := mux.NewRouter()
  65. // Authentication
  66. r.Handle("/login", loginHandler())
  67. r.Handle("/logout", logoutHandler())
  68. // Dashboard
  69. r.Handle("/", jwtMiddleware.Handler(recoverHandler(dashboardHandler())))
  70. // Generate model handlers
  71. for _, model := range []string{"teachers", "classes", "subjects", "activities"} {
  72. generateHandler(r, model)
  73. }
  74. for _, model := range []string{"teachers"} {
  75. generateAPIHandler(r, model)
  76. }
  77. // // Token handling
  78. // r.Handle("/get_token", getToken(db))
  79. // Static file server
  80. r.PathPrefix("/").Handler(http.FileServer(http.Dir("./dist/")))
  81. return r
  82. }
  83. func onError(w http.ResponseWriter, r *http.Request, err string) {
  84. http.Redirect(w, r, "/login", http.StatusTemporaryRedirect)
  85. }
  86. func respondWithStaticFile(w http.ResponseWriter, filename string) error {
  87. f, err := ioutil.ReadFile(filepath.Join("public/html", filename))
  88. if err != nil {
  89. return err
  90. }
  91. w.Write(f)
  92. return nil
  93. }
  94. func fromCookie(r *http.Request) (string, error) {
  95. session, err := store.Get(r, "login-session")
  96. if err != nil {
  97. return "", nil
  98. }
  99. if session.Values["token"] == nil {
  100. return "", nil
  101. }
  102. token := session.Values["token"].([]uint8)
  103. return string(token), nil
  104. }
  105. func recoverHandler(next http.Handler) http.Handler {
  106. fn := func(w http.ResponseWriter, r *http.Request) {
  107. defer func() {
  108. if err := recover(); err != nil {
  109. panicMsg := fmt.Sprintf("PANIC: %v\n\n== STACKTRACE ==\n%s", err, debug.Stack())
  110. log.Print(panicMsg)
  111. http.Error(w, panicMsg, http.StatusInternalServerError)
  112. }
  113. }()
  114. next.ServeHTTP(w, r)
  115. }
  116. return http.HandlerFunc(fn)
  117. }
  118. func modelHandler(base, path, redirectPath string) http.Handler {
  119. fn := func(w http.ResponseWriter, r *http.Request) {
  120. var (
  121. ok bool
  122. getFn orm.GetFn
  123. postFn orm.PostFn
  124. )
  125. switch r.Method {
  126. case "GET":
  127. getFn, ok = orm.Get[path]
  128. if !ok {
  129. renderer.Render[r.URL.Query()["format"][0]](w, r, fmt.Errorf("Can't find ORM function for path %s!", path))
  130. }
  131. data, err := getFn(mux.Vars(r))
  132. if err != nil {
  133. renderer.Render[r.URL.Query()["format"][0]](w, r, err)
  134. } else {
  135. renderer.Render[r.URL.Query()["format"][0]](w, r, data, r.URL.Query())
  136. }
  137. case "POST":
  138. postFn, ok = orm.Post[path]
  139. if !ok {
  140. renderer.Render[r.URL.Query()["format"][0]](w, r, fmt.Errorf("Can't find ORM function for path %s!", path))
  141. }
  142. data, err := postFn(mux.Vars(r), r)
  143. if err != nil {
  144. renderer.Render["html"](w, r, err)
  145. } else {
  146. if mux.Vars(r)["id"] != "" {
  147. http.Redirect(w, r,
  148. fmt.Sprintf(
  149. "/%s/%s?format=html&tpl_layout=base&tpl_content=%s_show",
  150. base,
  151. mux.Vars(r)["id"],
  152. base,
  153. ),
  154. http.StatusSeeOther,
  155. )
  156. } else {
  157. http.Redirect(w, r,
  158. fmt.Sprintf(
  159. "/%s/%d?format=html&tpl_layout=base&tpl_content=%s_show",
  160. base,
  161. data.GetID(),
  162. base,
  163. ),
  164. http.StatusSeeOther,
  165. )
  166. }
  167. }
  168. case "DELETE":
  169. postFn, ok = orm.Post[path]
  170. if !ok {
  171. renderer.Render[r.URL.Query().Get("format")](w, r, fmt.Errorf("Can't find ORM function for path %s!", path))
  172. }
  173. _, err := postFn(mux.Vars(r), r)
  174. if err != nil {
  175. renderer.Render["html"](w, r, err)
  176. } else {
  177. var data struct {
  178. RedirectUrl string `json:"redirect_url"`
  179. }
  180. data.RedirectUrl = fmt.Sprintf(redirectPath, base, base)
  181. w.Header().Set("Content-Type", "application/json")
  182. json.NewEncoder(w).Encode(data)
  183. }
  184. }
  185. }
  186. return http.HandlerFunc(fn)
  187. }