handlers.go 6.7 KB

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