|
@@ -6,9 +6,12 @@ import (
|
|
"io/ioutil"
|
|
"io/ioutil"
|
|
"log"
|
|
"log"
|
|
"net/http"
|
|
"net/http"
|
|
|
|
+ "path"
|
|
"path/filepath"
|
|
"path/filepath"
|
|
|
|
+ "reflect"
|
|
"runtime/debug"
|
|
"runtime/debug"
|
|
"strconv"
|
|
"strconv"
|
|
|
|
+ "strings"
|
|
|
|
|
|
"gogs.carducci-dante.gov.it/karmen/core/config"
|
|
"gogs.carducci-dante.gov.it/karmen/core/config"
|
|
"gogs.carducci-dante.gov.it/karmen/core/orm"
|
|
"gogs.carducci-dante.gov.it/karmen/core/orm"
|
|
@@ -18,6 +21,7 @@ import (
|
|
jwt "github.com/dgrijalva/jwt-go"
|
|
jwt "github.com/dgrijalva/jwt-go"
|
|
"github.com/gorilla/mux"
|
|
"github.com/gorilla/mux"
|
|
"github.com/gorilla/sessions"
|
|
"github.com/gorilla/sessions"
|
|
|
|
+ "github.com/jinzhu/inflection"
|
|
)
|
|
)
|
|
|
|
|
|
type User struct {
|
|
type User struct {
|
|
@@ -50,20 +54,6 @@ var (
|
|
},
|
|
},
|
|
SigningMethod: jwt.SigningMethodHS256,
|
|
SigningMethod: jwt.SigningMethodHS256,
|
|
})
|
|
})
|
|
-
|
|
|
|
- models = []string{
|
|
|
|
- "teachers",
|
|
|
|
- "classes",
|
|
|
|
- "subjects",
|
|
|
|
- "departments",
|
|
|
|
- "activities",
|
|
|
|
- "students",
|
|
|
|
- "offices",
|
|
|
|
- "administratives",
|
|
|
|
- "documents",
|
|
|
|
- "jobs",
|
|
|
|
- "groups",
|
|
|
|
- }
|
|
|
|
)
|
|
)
|
|
|
|
|
|
func (pp PathPattern) RedirectPath(model string, id ...uint) string {
|
|
func (pp PathPattern) RedirectPath(model string, id ...uint) string {
|
|
@@ -77,56 +67,63 @@ func (pp PathPattern) Path(model string) string {
|
|
return fmt.Sprintf(pp.PathPattern, model)
|
|
return fmt.Sprintf(pp.PathPattern, model)
|
|
}
|
|
}
|
|
|
|
|
|
-// Generate CRUD handlers
|
|
|
|
-func generateHandler(r *mux.Router, model string) {
|
|
|
|
|
|
+func modelName(s interface{}) string {
|
|
|
|
+ if t := reflect.TypeOf(s); t.Kind() == reflect.Ptr {
|
|
|
|
+ return t.Elem().Name()
|
|
|
|
+ } else {
|
|
|
|
+ return t.Name()
|
|
|
|
+ }
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+func pluralizedModelName(s interface{}) string {
|
|
|
|
+ return inflection.Plural(strings.ToLower(modelName(s)))
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+// Generate CRUD handlers for models
|
|
|
|
+func generateHandler(r *mux.Router, model interface{}) {
|
|
var (
|
|
var (
|
|
patterns []PathPattern = []PathPattern{
|
|
patterns []PathPattern = []PathPattern{
|
|
PathPattern{"/%s", "", []string{"GET"}},
|
|
PathPattern{"/%s", "", []string{"GET"}},
|
|
PathPattern{"/%s/{id}", "", []string{"GET"}},
|
|
PathPattern{"/%s/{id}", "", []string{"GET"}},
|
|
- PathPattern{"/%s/add/", "/%s/%d?format=html&tpl_layout=base&tpl_content=%s_show", []string{"GET", "POST"}},
|
|
|
|
|
|
+ PathPattern{"/%s/create/", "/%s/%d?format=html&tpl_layout=base&tpl_content=%s_show", []string{"GET", "POST"}},
|
|
PathPattern{"/%s/{id}/update", "/%s/%d?format=html&tpl_layout=base&tpl_content=%s_show", []string{"GET", "POST"}},
|
|
PathPattern{"/%s/{id}/update", "/%s/%d?format=html&tpl_layout=base&tpl_content=%s_show", []string{"GET", "POST"}},
|
|
PathPattern{"/%s/{id}/delete", "/%s?format=html&tpl_layout=base&tpl_content=%s", []string{"DELETE"}},
|
|
PathPattern{"/%s/{id}/delete", "/%s?format=html&tpl_layout=base&tpl_content=%s", []string{"DELETE"}},
|
|
}
|
|
}
|
|
|
|
|
|
- apiPatterns []PathPattern = []PathPattern{
|
|
|
|
- PathPattern{"/api/%s", "", []string{"GET"}},
|
|
|
|
- PathPattern{"/api/%s/{id}", "", []string{"GET"}},
|
|
|
|
- PathPattern{"/api/%s/add/", "", []string{"GET", "POST"}},
|
|
|
|
- PathPattern{"/api/%s/{id}/update", "", []string{"GET", "POST"}},
|
|
|
|
- PathPattern{"/api/%s/{id}/delete", "", []string{"DELETE"}},
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- executePatterns []PathPattern = []PathPattern{
|
|
|
|
- PathPattern{"/%s/{id}/execute", "", []string{"GET"}},
|
|
|
|
- }
|
|
|
|
|
|
+ apiPatterns []PathPattern
|
|
|
|
|
|
filePatterns []PathPattern = []PathPattern{
|
|
filePatterns []PathPattern = []PathPattern{
|
|
PathPattern{"/%s/{id}/files/{filename}", "", []string{"GET"}},
|
|
PathPattern{"/%s/{id}/files/{filename}", "", []string{"GET"}},
|
|
}
|
|
}
|
|
)
|
|
)
|
|
- for _, pattern := range patterns {
|
|
|
|
- r.Handle(pattern.Path(model), jwtCookie.Handler(recoverHandler(modelHandler(model, pattern)))).Methods(pattern.Methods...)
|
|
|
|
|
|
+
|
|
|
|
+ // Generate API patterns prefixing "api" path
|
|
|
|
+
|
|
|
|
+ for _, p := range patterns {
|
|
|
|
+ apiPatterns = append(apiPatterns, PathPattern{path.Join("/", "api", p.PathPattern), "", p.Methods})
|
|
}
|
|
}
|
|
|
|
|
|
- for _, pattern := range apiPatterns {
|
|
|
|
- r.Handle(pattern.Path(model), jwtHeader.Handler(recoverHandler(modelHandler(model, pattern)))).Methods(pattern.Methods...)
|
|
|
|
|
|
+ // Install standard paths
|
|
|
|
+
|
|
|
|
+ for _, pattern := range patterns {
|
|
|
|
+ r.Handle(pattern.Path(pluralizedModelName(model)), jwtCookie.Handler(recoverHandler(modelHandler(pluralizedModelName(model), pattern)))).Methods(pattern.Methods...)
|
|
}
|
|
}
|
|
|
|
|
|
- if model == "documents" {
|
|
|
|
- for _, pattern := range executePatterns {
|
|
|
|
- r.Handle(pattern.Path(model), jwtCookie.Handler(recoverHandler(modelHandler(model, pattern)))).Methods(pattern.Methods...)
|
|
|
|
- }
|
|
|
|
|
|
+ // Install API paths
|
|
|
|
+
|
|
|
|
+ for _, pattern := range apiPatterns {
|
|
|
|
+ r.Handle(pattern.Path(pluralizedModelName(model)), jwtHeader.Handler(recoverHandler(modelHandler(pluralizedModelName(model), pattern)))).Methods(pattern.Methods...)
|
|
}
|
|
}
|
|
|
|
|
|
- if model == "jobs" {
|
|
|
|
|
|
+ if modelName(model) == "Job" {
|
|
for _, pattern := range filePatterns {
|
|
for _, pattern := range filePatterns {
|
|
- r.Handle(pattern.Path(model), jwtCookie.Handler(recoverHandler(modelHandler(model, pattern)))).Methods(pattern.Methods...)
|
|
|
|
|
|
+ r.Handle(pattern.Path(pluralizedModelName(model)), jwtCookie.Handler(recoverHandler(modelHandler(pluralizedModelName(model), pattern)))).Methods(pattern.Methods...)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
}
|
|
}
|
|
|
|
|
|
-func Handlers() *mux.Router {
|
|
|
|
|
|
+func Handlers(models []interface{}) *mux.Router {
|
|
r := mux.NewRouter()
|
|
r := mux.NewRouter()
|
|
|
|
|
|
// Authentication
|
|
// Authentication
|
|
@@ -138,16 +135,20 @@ func Handlers() *mux.Router {
|
|
|
|
|
|
r.Handle("/", jwtCookie.Handler(recoverHandler(homeHandler())))
|
|
r.Handle("/", jwtCookie.Handler(recoverHandler(homeHandler())))
|
|
|
|
|
|
- // Generate model handlers
|
|
|
|
|
|
+ // Generate CRUD handlers
|
|
|
|
|
|
for _, model := range models {
|
|
for _, model := range models {
|
|
generateHandler(r, model)
|
|
generateHandler(r, model)
|
|
}
|
|
}
|
|
|
|
|
|
|
|
+ r.Handle("/documents/{id}/execute", jwtCookie.Handler(recoverHandler(executeHandler()))).Methods("GET")
|
|
|
|
+
|
|
// Token handling
|
|
// Token handling
|
|
|
|
+
|
|
r.Handle("/get_token", tokenHandler())
|
|
r.Handle("/get_token", tokenHandler())
|
|
|
|
|
|
// Static file server
|
|
// Static file server
|
|
|
|
+
|
|
r.PathPrefix("/").Handler(http.FileServer(http.Dir("./dist/")))
|
|
r.PathPrefix("/").Handler(http.FileServer(http.Dir("./dist/")))
|
|
|
|
|
|
return r
|
|
return r
|
|
@@ -198,11 +199,11 @@ func recoverHandler(next http.Handler) http.Handler {
|
|
func get(w http.ResponseWriter, r *http.Request, model string, pattern PathPattern) {
|
|
func get(w http.ResponseWriter, r *http.Request, model string, pattern PathPattern) {
|
|
format := r.URL.Query().Get("format")
|
|
format := r.URL.Query().Get("format")
|
|
getFn, err := orm.GetFunc(pattern.Path(model))
|
|
getFn, err := orm.GetFunc(pattern.Path(model))
|
|
-
|
|
|
|
if err != nil {
|
|
if err != nil {
|
|
|
|
+ log.Println("Error:", err)
|
|
respondWithError(w, r, err)
|
|
respondWithError(w, r, err)
|
|
} else {
|
|
} else {
|
|
- data, err := getFn(mux.Vars(r))
|
|
|
|
|
|
+ data, err := getFn(mux.Vars(r), r)
|
|
if err != nil {
|
|
if err != nil {
|
|
renderer.Render[format](w, r, err)
|
|
renderer.Render[format](w, r, err)
|
|
} else {
|
|
} else {
|
|
@@ -212,20 +213,14 @@ func get(w http.ResponseWriter, r *http.Request, model string, pattern PathPatte
|
|
|
|
|
|
}
|
|
}
|
|
|
|
|
|
-func respondWithError(w http.ResponseWriter, r *http.Request, err error) {
|
|
|
|
- respFormat := renderer.GetContentFormat(r)
|
|
|
|
- w.WriteHeader(http.StatusInternalServerError)
|
|
|
|
- renderer.Render[respFormat](w, r, err)
|
|
|
|
-}
|
|
|
|
-
|
|
|
|
func post(w http.ResponseWriter, r *http.Request, model string, pattern PathPattern) {
|
|
func post(w http.ResponseWriter, r *http.Request, model string, pattern PathPattern) {
|
|
var (
|
|
var (
|
|
- data orm.IDer
|
|
|
|
|
|
+ data interface{}
|
|
err error
|
|
err error
|
|
)
|
|
)
|
|
|
|
|
|
respFormat := renderer.GetContentFormat(r)
|
|
respFormat := renderer.GetContentFormat(r)
|
|
- postFn, err := orm.PostFunc(pattern.Path(model))
|
|
|
|
|
|
+ postFn, err := orm.GetFunc(pattern.Path(model))
|
|
|
|
|
|
if err != nil {
|
|
if err != nil {
|
|
respondWithError(w, r, err)
|
|
respondWithError(w, r, err)
|
|
@@ -238,10 +233,10 @@ func post(w http.ResponseWriter, r *http.Request, model string, pattern PathPatt
|
|
modelId, _ := strconv.Atoi(id)
|
|
modelId, _ := strconv.Atoi(id)
|
|
http.Redirect(w, r, pattern.RedirectPath(model, uint(modelId)), http.StatusSeeOther)
|
|
http.Redirect(w, r, pattern.RedirectPath(model, uint(modelId)), http.StatusSeeOther)
|
|
} else {
|
|
} else {
|
|
- http.Redirect(w, r, pattern.RedirectPath(model, data.GetID()), http.StatusSeeOther)
|
|
|
|
|
|
+ http.Redirect(w, r, pattern.RedirectPath(model, data.(orm.IDer).GetID()), http.StatusSeeOther)
|
|
}
|
|
}
|
|
} else {
|
|
} else {
|
|
- renderer.Render[respFormat](w, r, data.GetID())
|
|
|
|
|
|
+ renderer.Render[respFormat](w, r, data.(orm.IDer).GetID())
|
|
}
|
|
}
|
|
|
|
|
|
}
|
|
}
|
|
@@ -250,15 +245,15 @@ func post(w http.ResponseWriter, r *http.Request, model string, pattern PathPatt
|
|
|
|
|
|
func delete(w http.ResponseWriter, r *http.Request, model string, pattern PathPattern) {
|
|
func delete(w http.ResponseWriter, r *http.Request, model string, pattern PathPattern) {
|
|
var (
|
|
var (
|
|
- data orm.IDer
|
|
|
|
|
|
+ data interface{}
|
|
err error
|
|
err error
|
|
)
|
|
)
|
|
|
|
|
|
respFormat := renderer.GetContentFormat(r)
|
|
respFormat := renderer.GetContentFormat(r)
|
|
|
|
|
|
- postFn, ok := orm.Post[pattern.Path(model)]
|
|
|
|
- if !ok {
|
|
|
|
- renderer.Render[r.URL.Query().Get("format")](w, r, fmt.Errorf("Can't find ORM function for path %s!", pattern.PathPattern))
|
|
|
|
|
|
+ postFn, err := orm.GetFunc(pattern.Path(model))
|
|
|
|
+ if err != nil {
|
|
|
|
+ renderer.Render[r.URL.Query().Get("format")](w, r, err)
|
|
}
|
|
}
|
|
data, err = postFn(mux.Vars(r), r)
|
|
data, err = postFn(mux.Vars(r), r)
|
|
if err != nil {
|
|
if err != nil {
|
|
@@ -272,12 +267,22 @@ func delete(w http.ResponseWriter, r *http.Request, model string, pattern PathPa
|
|
w.Header().Set("Content-Type", "application/json")
|
|
w.Header().Set("Content-Type", "application/json")
|
|
json.NewEncoder(w).Encode(data)
|
|
json.NewEncoder(w).Encode(data)
|
|
} else {
|
|
} else {
|
|
- renderer.Render[respFormat](w, r, data.GetID())
|
|
|
|
|
|
+ renderer.Render[respFormat](w, r, data.(orm.IDer).GetID())
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
|
|
+func respondWithError(w http.ResponseWriter, r *http.Request, err error) {
|
|
|
|
+ respFormat := renderer.GetContentFormat(r)
|
|
|
|
+ w.WriteHeader(http.StatusInternalServerError)
|
|
|
|
+ renderer.Render[respFormat](w, r, err)
|
|
|
|
+}
|
|
|
|
+
|
|
func modelHandler(model string, pattern PathPattern) http.Handler {
|
|
func modelHandler(model string, pattern PathPattern) http.Handler {
|
|
fn := func(w http.ResponseWriter, r *http.Request) {
|
|
fn := func(w http.ResponseWriter, r *http.Request) {
|
|
|
|
+
|
|
|
|
+ // Replace "api" prefix
|
|
|
|
+ pattern.PathPattern = strings.Replace(pattern.PathPattern, "/api", "", -1)
|
|
|
|
+
|
|
switch r.Method {
|
|
switch r.Method {
|
|
case "GET":
|
|
case "GET":
|
|
get(w, r, model, pattern)
|
|
get(w, r, model, pattern)
|
|
@@ -287,13 +292,26 @@ func modelHandler(model string, pattern PathPattern) http.Handler {
|
|
|
|
|
|
case "DELETE":
|
|
case "DELETE":
|
|
delete(w, r, model, pattern)
|
|
delete(w, r, model, pattern)
|
|
- break
|
|
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
return http.HandlerFunc(fn)
|
|
return http.HandlerFunc(fn)
|
|
}
|
|
}
|
|
|
|
|
|
|
|
+func executeHandler() http.Handler {
|
|
|
|
+ fn := func(w http.ResponseWriter, r *http.Request) {
|
|
|
|
+ format := r.URL.Query().Get("format")
|
|
|
|
+ data, err := orm.GetDocumentExecute(mux.Vars(r), r)
|
|
|
|
+ if err != nil {
|
|
|
|
+ renderer.Render[format](w, r, err)
|
|
|
|
+ } else {
|
|
|
|
+ renderer.Render[format](w, r, data, r.URL.Query())
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ }
|
|
|
|
+ return http.HandlerFunc(fn)
|
|
|
|
+}
|
|
|
|
+
|
|
func homeHandler() http.Handler {
|
|
func homeHandler() http.Handler {
|
|
fn := func(w http.ResponseWriter, r *http.Request) {
|
|
fn := func(w http.ResponseWriter, r *http.Request) {
|
|
http.Redirect(w, r, "/teachers?format=html&tpl_layout=teachers&tpl_content=teachers", http.StatusSeeOther)
|
|
http.Redirect(w, r, "/teachers?format=html&tpl_layout=teachers&tpl_content=teachers", http.StatusSeeOther)
|