func (s *WalkthroughService) walkthroughDeleteHandler() http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { id := hitch.Params(r).ByName("id") db := ab.GetDB(r) abort := false loadFunc := LoadWalkthrough loadFunc = beforeWalkthroughDeleteHandler() if abort { return } entity, err := loadFunc(db, id) ab.MaybeFail(r, http.StatusInternalServerError, ab.ConvertDBError(err, walkthroughDBErrorConverter)) if entity == nil { ab.Fail(r, http.StatusNotFound, nil) } insideWalkthroughDeleteHandler(r, entity, db) if abort { return } err = entity.Delete(db) ab.MaybeFail(r, http.StatusInternalServerError, ab.ConvertDBError(err, walkthroughDBErrorConverter)) // HOOK: afterWalkthroughDeleteHandler() if abort { return } }) }
func (s *WalkthroughService) walkthroughPostHandler() http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { entity := &Walkthrough{} ab.MustDecode(r, entity) abort := false walkthroughPostValidation(entity, r) if abort { return } if err := entity.Validate(); err != nil { ab.Fail(r, http.StatusBadRequest, err) } db := ab.GetDB(r) err := entity.Insert(db) ab.MaybeFail(r, http.StatusInternalServerError, ab.ConvertDBError(err, walkthroughDBErrorConverter)) afterWalkthroughPostInsertHandler(db, s, entity) if abort { return } ab.Render(r).SetCode(http.StatusCreated).JSON(entity) }) }
func (s *WalkthroughService) walkthroughPutHandler() http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { id := hitch.Params(r).ByName("id") entity := &Walkthrough{} ab.MustDecode(r, entity) if err := entity.Validate(); entity.UUID != id || err != nil { ab.Fail(r, http.StatusBadRequest, err) } db := ab.GetDB(r) abort := false beforeWalkthroughPutUpdateHandler(r, entity, db) if abort { return } err := entity.Update(db) ab.MaybeFail(r, http.StatusInternalServerError, ab.ConvertDBError(err, walkthroughDBErrorConverter)) afterWalkthroughPutUpdateHandler(s, entity) if abort { return } ab.Render(r).JSON(entity) }) }
func (s *UserService) userPutHandler() http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { id := hitch.Params(r).ByName("id") entity := &User{} ab.MustDecode(r, entity) if err := entity.Validate(); entity.UUID != id || err != nil { ab.Fail(r, http.StatusBadRequest, err) } db := ab.GetDB(r) abort := false // HOOK: beforeUserPutUpdateHandler() if abort { return } err := entity.Update(db) ab.MaybeFail(r, http.StatusInternalServerError, ab.ConvertDBError(err, userDBErrorConverter)) // HOOK: afterUserPutUpdateHandler() if abort { return } ab.Render(r).JSON(entity) }) }
func (s *UserService) userGetHandler() http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { id := hitch.Params(r).ByName("id") db := ab.GetDB(r) abort := false loadFunc := LoadUser // HOOK: beforeUserGetHandler() if abort { return } entity, err := loadFunc(db, id) ab.MaybeFail(r, http.StatusInternalServerError, ab.ConvertDBError(err, userDBErrorConverter)) if entity == nil { ab.Fail(r, http.StatusNotFound, nil) } // HOOK: afterUserGetHandler() if abort { return } ab.Render(r).JSON(entity) }) }
func (s *UserService) userPostHandler() http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { entity := &User{} ab.MustDecode(r, entity) abort := false // HOOK: userPostValidation() if abort { return } if err := entity.Validate(); err != nil { ab.Fail(r, http.StatusBadRequest, err) } db := ab.GetDB(r) err := entity.Insert(db) ab.MaybeFail(r, http.StatusInternalServerError, ab.ConvertDBError(err, userDBErrorConverter)) // HOOK: afterUserPostInsertHandler() if abort { return } ab.Render(r).SetCode(http.StatusCreated).JSON(entity) }) }
func userLoggedInMiddleware(next http.Handler) http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { if !UserDelegate.IsLoggedIn(r) { ab.Fail(r, http.StatusForbidden, errors.New("user is not logged in")) } next.ServeHTTP(w, r) }) }
func insideWalkthroughDeleteHandler(r *http.Request, entity *Walkthrough, db ab.DB) { uid := UserDelegate.CurrentUser(r) currentUser, err := LoadUser(db, uid) ab.MaybeFail(r, http.StatusBadRequest, err) if entity.UID != uid { if !currentUser.Admin { ab.Fail(r, http.StatusForbidden, nil) } } }
func corsPreflightHandler(baseURL, httpOrigin string) http.Handler { baseurl, err := url.Parse(baseURL) if err != nil { panic(err) } httporigin, err := url.Parse(httpOrigin) if err != nil { panic(err) } return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { origin := r.Header.Get("Origin") method := r.Header.Get("Access-Control-Request-Method") headers := r.Header.Get("Access-Control-Request-Headers") w.Header().Add("Vary", "Origin") w.Header().Add("Vary", "Access-Control-Request-Method") w.Header().Add("Vary", "Access-Control-Request-Headers") if origin == "" || method == "" { ab.Fail(r, http.StatusBadRequest, nil) } originurl, err := url.Parse(origin) ab.MaybeFail(r, http.StatusBadRequest, err) if originurl.Host != baseurl.Host && originurl.Host != httporigin.Host { ab.Fail(r, http.StatusForbidden, nil) } w.Header().Set("Access-Control-Allow-Origin", origin) w.Header().Set("Access-Control-Allow-Methods", method) w.Header().Set("Access-Control-Allow-Headers", headers) w.Header().Set("Access-Control-Allow-Credentials", "true") w.Header().Set("Access-Control-Max-Age", "0") ab.Render(r).SetCode(http.StatusOK) }) }
func walkthroughPostValidation(entity *Walkthrough, r *http.Request) { uid := UserDelegate.CurrentUser(r) if entity.UID == "" { entity.UID = uid } if entity.UID != uid { ab.Fail(r, http.StatusBadRequest, errors.New("invalid user id")) } entity.Updated = time.Now() entity.Revision = "" entity.UUID = "" }
func beforeWalkthroughPutUpdateHandler(r *http.Request, entity *Walkthrough, db ab.DB) { uid := UserDelegate.CurrentUser(r) currentUser, err := LoadUser(db, uid) ab.MaybeFail(r, http.StatusBadRequest, err) if entity.UID != uid { if !currentUser.Admin { ab.Fail(r, http.StatusForbidden, nil) } } previousRevision, err := LoadActualRevision(db, entity.UUID) ab.MaybeFail(r, http.StatusBadRequest, err) if previousRevision == nil { ab.Fail(r, http.StatusNotFound, nil) } if previousRevision.UID != uid && !currentUser.Admin { ab.Fail(r, http.StatusForbidden, nil) } entity.Updated = time.Now() entity.Revision = "" }
func afterWalkthroughServiceRegister(s *WalkthroughService, h *hitch.Hitch) { reindexing := false var reindexingMutex sync.RWMutex h.Post("/api/reindexwalkthroughs", http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { reindexingMutex.RLock() idxing := reindexing reindexingMutex.RUnlock() if idxing { ab.Fail(r, http.StatusServiceUnavailable, errors.New("reindexing is in progress")) } reindexingMutex.Lock() reindexing = true reindexingMutex.Unlock() db := ab.GetDB(r) go func() { defer func() { reindexingMutex.Lock() reindexing = false reindexingMutex.Unlock() }() err := s.SearchService.PurgeIndex() if err != nil { log.Println(err) return } wts, err := LoadAllActualWalkthroughs(db, 0, 0) if err != nil { log.Println(err) return } for _, wt := range wts { err = s.SearchService.IndexEntity("walkthrough", wt) if err != nil { log.Println(err) return } } }() ab.Render(r).SetCode(http.StatusAccepted) }), ab.RestrictPrivateAddressMiddleware()) }
func (s *LogService) Register(h *hitch.Hitch) error { walkthroughPlayed := prometheus.NewCounter(stdprometheus.CounterOpts{ Namespace: "walkhub", Subsystem: "metrics", Name: "walkthrough_played", Help: "Number of walkthrough plays", }, []string{"uuid", "embedorigin"}) walkthroughVisited := prometheus.NewCounter(stdprometheus.CounterOpts{ Namespace: "walkhub", Subsystem: "metrics", Name: "walkthrough_visited", Help: "Number of walkthrough visits", }, []string{"uuid", "embedorigin"}) h.Post("/api/log/helpcenteropened", http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { l := helpCenterOpenedLog{} ab.MustDecode(r, &l) db := ab.GetDB(r) userid := getLogUserID(r) message := fmt.Sprintf("%s has opened the help center on %s", userid, l.URL) ab.MaybeFail(r, http.StatusInternalServerError, DBLog(db, "helpcenteropened", message)) })) h.Post("/api/log/walkthroughplayed", http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { l := walkthroughPlayedLog{} ab.MustDecode(r, &l) db := ab.GetDB(r) userid := getLogUserID(r) wt, err := LoadActualRevision(db, l.UUID) ab.MaybeFail(r, http.StatusBadRequest, err) if wt == nil { ab.Fail(r, http.StatusNotFound, nil) } message := "" embedPart := "" if l.EmbedOrigin != "" { embedPart = "from the help center on " + l.EmbedOrigin + " " } wturl := s.BaseURL + "walkthrough/" + wt.UUID if l.ErrorMessage == "" { message = fmt.Sprintf("%s has played the walkthrough %s<%s|%s>", userid, embedPart, wturl, wt.Name) } else { message = fmt.Sprintf("%s has failed to play the walkthrough %s<%s|%s> with the error message %s", userid, embedPart, wturl, wt.Name, l.ErrorMessage) } ab.MaybeFail(r, http.StatusInternalServerError, DBLog(db, "walkthroughplayed", message)) walkthroughPlayed. With(metrics.Field{Key: "uuid", Value: l.UUID}). With(metrics.Field{Key: "embedorigin", Value: l.EmbedOrigin}). Add(1) })) h.Post("/api/log/walkthroughpagevisited", http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { l := walkthroughPageVisitedLog{} ab.MustDecode(r, &l) db := ab.GetDB(r) userid := getLogUserID(r) wt, err := LoadActualRevision(db, l.UUID) ab.MaybeFail(r, http.StatusBadRequest, err) if wt == nil { ab.Fail(r, http.StatusNotFound, nil) } embedPart := "" if l.EmbedOrigin != "" { embedPart = "embedded on " + l.EmbedOrigin + " " } wturl := s.BaseURL + "walkthrough/" + wt.UUID message := fmt.Sprintf("%s has visited the walkthrough page %s<%s|%s>", userid, embedPart, wturl, wt.Name) ab.MaybeFail(r, http.StatusInternalServerError, DBLog(db, "walkthroughvisited", message)) walkthroughVisited. With(metrics.Field{Key: "uuid", Value: l.UUID}). With(metrics.Field{Key: "embedorigin", Value: l.EmbedOrigin}). Add(1) })) return nil }