package v1 import ( "crypto/md5" "database/sql" "encoding/hex" "encoding/json" "fmt" "io/ioutil" "log" "net/http" "strconv" "strings" "github.com/dgrijalva/jwt-go" "github.com/getsentry/sentry-go" "github.com/gorilla/mux" ) var lpServer *LongPollServer func RegisterRouters(r *mux.Router) { sub := r.PathPrefix("/v1").Subrouter() lpServer = InitLongPollServer() sub.Use(apiMiddleware) sub.Use(LimitMiddleware) go CleanupVisitors() sub.Use(JWTMiddleware) sub.HandleFunc("/version", func(w http.ResponseWriter, _ *http.Request) { Response(w, nil, map[string]string{ "version": config.Version, "build": strconv.Itoa(int(config.Build)), }) }) RegisterAuthRoutes(sub) RegisterUsersRoutes(sub) RegisterNovelsRoutes(sub) RegisterCharactersRoutes(sub) RegisterGenresRoutes(sub) RegisterGamesRoutes(sub) RegisterClubRoutes(sub) RegisterGroupsRoutes(sub) RegisterNewsRoutes(sub) RegisterTokensRouter(sub) RegisterLongPollRoutes(sub) if config.Debug { err := sub.Walk(func(route *mux.Route, _ *mux.Router, _ []*mux.Route) error { path, err := route.GetPathTemplate() if err != nil { return err } log.Printf("loading path %s\n", path) return nil }) if err != nil { log.Fatalln(err) } } } func Response(w http.ResponseWriter, err error, data interface{}) { const baseResponse = `{"error":false,"data":%v}` if err != nil { if err == sql.ErrNoRows { NotFound.Ferror(w) } else { w.WriteHeader(http.StatusInternalServerError) Error(w, err) } } else { j, err := json.Marshal(&data) if err != nil { fmt.Println(err) sentry.CaptureException(err) return } _, err = fmt.Fprintf(w, baseResponse, string(j)) if err != nil { fmt.Println(err) sentry.CaptureException(err) } } } func ResponsePagination(w http.ResponseWriter, err error, data interface{}, pagination interface{}) { const baseResponse = `{"error":false,"pagination":%v,"data":%v}` if err != nil { if err == sql.ErrNoRows { NotFound.Ferror(w) } else { w.WriteHeader(http.StatusInternalServerError) Error(w, err) } } else { jsonData, err := json.Marshal(&data) if err != nil { fmt.Println(err) sentry.CaptureException(err) return } jsonPagination, err := json.Marshal(&pagination) if err != nil { fmt.Println(err) sentry.CaptureException(err) return } _, err = fmt.Fprintf(w, baseResponse, string(jsonPagination), string(jsonData)) if err != nil { fmt.Println(err) sentry.CaptureException(err) } } } func RawError(w http.ResponseWriter, e string) { const baseError = `{"error":true,"error_data":{"code":-1,"text":"%v"}}` _, err := fmt.Fprintf(w, baseError, strings.ReplaceAll(e, `"`, `\"`)) if err != nil { fmt.Println(err) sentry.CaptureException(err) } } func Error(w http.ResponseWriter, e error) { const baseError = `{"error":true,"error_data":{"code":-1,"text":"%v"}}` _, err := fmt.Fprintf(w, baseError, strings.ReplaceAll(e.Error(), `"`, `\"`)) if err != nil { fmt.Println(err) sentry.CaptureException(err) } } func JSON(w http.ResponseWriter, data string) { _, err := fmt.Fprint(w, data) if err != nil { fmt.Println(err) sentry.CaptureException(err) } } func ReadBody(r *http.Request) ([]byte, error) { body, err := ioutil.ReadAll(r.Body) if err != nil { return nil, err } err = r.Body.Close() return body, err } func MD5(s string) string { hash := md5.New() hash.Write([]byte(s)) return hex.EncodeToString(hash.Sum(nil)) } func GetCurrentUserID(r *http.Request) (uint64, error) { tokenCookie, err := r.Cookie("token") if err != nil { return 0, err } token, err := jwt.Parse(tokenCookie.Value, func(token *jwt.Token) (interface{}, error) { if token.Method != jwt.SigningMethodHS256 { return nil, fmt.Errorf("invalid singning method: %s", token.Method) } return []byte(privateConfig.AuthTokenSecret), nil }) if err != nil { return 0, err } claims := token.Claims.(jwt.MapClaims) id := int(claims["id"].(float64)) return uint64(id), nil } func apiMiddleware(next http.Handler) http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { if r.Header.Get("Dev") == "1" { w.Header().Add("Access-Control-Allow-Origin", config.DevFrontendUrl) } else { w.Header().Add("Access-Control-Allow-Origin", config.FrontendUrl) } w.Header().Add("Access-Control-Allow-Credentials", "true") w.Header().Add("Content-Type", "application/json; charset=utf-8") //w.Header().Add("Cache-Control", "max-age=60") if r.Method == http.MethodOptions { w.Header().Add("Access-Control-Allow-Headers", "Content-Type, Date, Content-Length, Location, dev, cache") w.Header().Add("Access-Control-Allow-Methods", "GET, POST, DELETE, PUT, OPTIONS") return } if database == nil { log.Println("db not initialized") return } err := database.Ping() if err != nil && strings.Contains(err.Error(), "broken pipe") { ConnectToDB() log.Println("connection to db reopened") } next.ServeHTTP(w, r) }) }