diff --git a/app/main.go b/app/main.go index 7cd56008..6f404353 100644 --- a/app/main.go +++ b/app/main.go @@ -8,6 +8,7 @@ import ( "time" "github.com/umputun/remark/app/notifier" + "github.com/umputun/remark/app/rest" "github.com/gorilla/sessions" "github.com/hashicorp/logutils" @@ -15,10 +16,8 @@ import ( "github.com/pkg/errors" "github.com/umputun/remark/app/migrator" - "github.com/umputun/remark/app/rest" "github.com/umputun/remark/app/rest/auth" - "github.com/umputun/remark/app/rest/avatar" - "github.com/umputun/remark/app/rest/common" + "github.com/umputun/remark/app/rest/server" "github.com/umputun/remark/app/store" ) @@ -100,13 +99,13 @@ func main() { exporter := migrator.Remark{DataStore: dataStore} - avatarProxy := &avatar.Proxy{ + avatarProxy := &auth.AvatarProxy{ StorePath: opts.ServerCommand.AvatarStore, RoutePath: "/api/v1/avatar", DefaultAvatar: opts.ServerCommand.DefaultAvatar, } - srv := rest.Server{ + srv := server.Rest{ Version: revision, DataService: dataService, DevMode: opts.DevMode, @@ -117,7 +116,7 @@ func main() { Providers: makeAuthProviders(sessionStore, avatarProxy), AvatarProxy: avatarProxy, }, - Cache: common.NewLoadingCache(4*time.Hour, 15*time.Minute, postFlushFn), + Cache: rest.NewLoadingCache(4*time.Hour, 15*time.Minute, postFlushFn), Notifier: notifier.NewNoperation(), } @@ -180,7 +179,7 @@ func makeDirs(dirs ...string) error { return nil } -func makeAuthProviders(sessionStore sessions.Store, avatarProxy *avatar.Proxy) []auth.Provider { +func makeAuthProviders(sessionStore sessions.Store, avatarProxy *auth.AvatarProxy) []auth.Provider { providers := []auth.Provider{} srvOpts := opts.ServerCommand if srvOpts.GoogleCID != "" && srvOpts.GoogleCSEC != "" { diff --git a/app/rest/auth/auth.go b/app/rest/auth/auth.go index a42eaf21..52f00f03 100644 --- a/app/rest/auth/auth.go +++ b/app/rest/auth/auth.go @@ -6,15 +6,14 @@ import ( "github.com/gorilla/sessions" - "github.com/umputun/remark/app/rest/avatar" - "github.com/umputun/remark/app/rest/common" + "github.com/umputun/remark/app/rest" "github.com/umputun/remark/app/store" ) // Authenticator is top level auth object providing middlewares type Authenticator struct { SessionStore sessions.Store - AvatarProxy *avatar.Proxy + AvatarProxy *AvatarProxy Admins []string Providers []Provider } @@ -56,7 +55,7 @@ func (a *Authenticator) Auth(modes []Mode) func(http.Handler) http.Handler { if inModes(Developer) { user := devUser ctx := r.Context() - ctx = context.WithValue(ctx, common.ContextKey("user"), user) + ctx = context.WithValue(ctx, rest.ContextKey("user"), user) r = r.WithContext(ctx) h.ServeHTTP(w, r) return @@ -89,7 +88,7 @@ func (a *Authenticator) Auth(modes []Mode) func(http.Handler) http.Handler { } ctx := r.Context() - ctx = context.WithValue(ctx, common.ContextKey("user"), user) + ctx = context.WithValue(ctx, rest.ContextKey("user"), user) r = r.WithContext(ctx) } h.ServeHTTP(w, r) @@ -103,7 +102,7 @@ func (a *Authenticator) Auth(modes []Mode) func(http.Handler) http.Handler { func (a *Authenticator) AdminOnly(next http.Handler) http.Handler { fn := func(w http.ResponseWriter, r *http.Request) { - user, err := common.GetUserInfo(r) + user, err := rest.GetUserInfo(r) if err != nil { http.Error(w, "Unauthorized", http.StatusUnauthorized) return diff --git a/app/rest/avatar/avatar.go b/app/rest/auth/avatar.go similarity index 83% rename from app/rest/avatar/avatar.go rename to app/rest/auth/avatar.go index f35cbe76..571e5d39 100644 --- a/app/rest/avatar/avatar.go +++ b/app/rest/auth/avatar.go @@ -1,6 +1,4 @@ -// Package avatar provides cached proxy for user pictures/avatars -// refreshed by login and kept in local store -package avatar +package auth import ( "crypto/sha1" @@ -18,12 +16,12 @@ import ( "github.com/go-chi/render" "github.com/pkg/errors" - "github.com/umputun/remark/app/rest/common" + "github.com/umputun/remark/app/rest" "github.com/umputun/remark/app/store" ) -// Proxy provides avatar store and http handler for avatars -type Proxy struct { +// AvatarProxy provides avatar store and http handler for avatars +type AvatarProxy struct { StorePath string DefaultAvatar string RoutePath string @@ -32,7 +30,7 @@ type Proxy struct { const imgSfx = ".image" // Put gets original avatar url from user info and returns proxied url -func (p *Proxy) Put(u store.User) (avatarURL string, err error) { +func (p *AvatarProxy) Put(u store.User) (avatarURL string, err error) { if u.Picture == "" { if p.DefaultAvatar != "" { @@ -83,7 +81,7 @@ func (p *Proxy) Put(u store.User) (avatarURL string, err error) { } // Routes returns auth routes for given provider -func (p *Proxy) Routes() (string, chi.Router) { +func (p *AvatarProxy) Routes() (string, chi.Router) { router := chi.NewRouter() // GET /123456789.image @@ -94,11 +92,11 @@ func (p *Proxy) Routes() (string, chi.Router) { fh, err := os.Open(avFile) if err != nil { if p.DefaultAvatar == "" { - common.SendErrorJSON(w, r, http.StatusBadRequest, err, "can't load avatar") + rest.SendErrorJSON(w, r, http.StatusBadRequest, err, "can't load avatar") return } if fh, err = os.Open(path.Join(p.StorePath, p.DefaultAvatar)); err != nil { - common.SendErrorJSON(w, r, http.StatusBadRequest, err, "can't load default avatar") + rest.SendErrorJSON(w, r, http.StatusBadRequest, err, "can't load default avatar") return } } @@ -122,7 +120,7 @@ func (p *Proxy) Routes() (string, chi.Router) { } // encodeID hashes user id to sha1 -func (p *Proxy) encodeID(id string) string { +func (p *AvatarProxy) encodeID(id string) string { h := sha1.New() _, err := h.Write([]byte(id)) if err != nil { @@ -133,7 +131,7 @@ func (p *Proxy) encodeID(id string) string { // get location for user id by adding partion to final path // the end result is a full path like this - /tmp/avatars.test/92 -func (p *Proxy) location(id string) string { +func (p *AvatarProxy) location(id string) string { checksum64 := crc64.Checksum([]byte(id), crc64.MakeTable(crc64.ECMA)) partition := checksum64 % 100 return path.Join(p.StorePath, fmt.Sprintf("%02d", partition)) diff --git a/app/rest/avatar/avatar_test.go b/app/rest/auth/avatar_test.go similarity index 88% rename from app/rest/avatar/avatar_test.go rename to app/rest/auth/avatar_test.go index b1e58b6a..af90460d 100644 --- a/app/rest/avatar/avatar_test.go +++ b/app/rest/auth/avatar_test.go @@ -1,4 +1,4 @@ -package avatar +package auth import ( "bytes" @@ -15,7 +15,7 @@ import ( ) func TestPut(t *testing.T) { - p := Proxy{StorePath: "/tmp/avatars.test", RoutePath: "/avatar"} + p := AvatarProxy{StorePath: "/tmp/avatars.test", RoutePath: "/avatar"} os.MkdirAll("/tmp/avatars.test", 0700) defer os.RemoveAll("/tmp/avatars.test") @@ -37,7 +37,7 @@ func TestPut(t *testing.T) { } func TestPutDefault(t *testing.T) { - p := Proxy{StorePath: "/tmp/avatars.test", RoutePath: "/avatar", DefaultAvatar: "default.image"} + p := AvatarProxy{StorePath: "/tmp/avatars.test", RoutePath: "/avatar", DefaultAvatar: "default.image"} os.MkdirAll("/tmp/avatars.test", 0700) ioutil.WriteFile("/tmp/avatars.test/default.image", []byte("1234567890"), 0600) defer os.RemoveAll("/tmp/avatars.test") @@ -52,7 +52,7 @@ func TestPutDefault(t *testing.T) { } func TestRoutes(t *testing.T) { - p := Proxy{StorePath: "/tmp/avatars.test", RoutePath: "/avatar", DefaultAvatar: "default.image"} + p := AvatarProxy{StorePath: "/tmp/avatars.test", RoutePath: "/avatar", DefaultAvatar: "default.image"} os.MkdirAll("/tmp/avatars.test", 0700) defer os.RemoveAll("/tmp/avatars.test") @@ -78,7 +78,7 @@ func TestRoutes(t *testing.T) { assert.Equal(t, int64(8432), sz) } func TestRoutesDefault(t *testing.T) { - p := Proxy{StorePath: "/tmp/avatars.test", RoutePath: "/avatar", DefaultAvatar: "default.image"} + p := AvatarProxy{StorePath: "/tmp/avatars.test", RoutePath: "/avatar", DefaultAvatar: "default.image"} os.MkdirAll("/tmp/avatars.test", 0700) ioutil.WriteFile("/tmp/avatars.test/default.image", []byte("1234567890"), 0600) defer os.RemoveAll("/tmp/avatars.test") diff --git a/app/rest/auth/provider.go b/app/rest/auth/provider.go index eb34bd09..54d9352a 100644 --- a/app/rest/auth/provider.go +++ b/app/rest/auth/provider.go @@ -17,8 +17,7 @@ import ( "github.com/gorilla/sessions" "golang.org/x/oauth2" - "github.com/umputun/remark/app/rest/avatar" - "github.com/umputun/remark/app/rest/common" + "github.com/umputun/remark/app/rest" "github.com/umputun/remark/app/store" ) @@ -33,7 +32,7 @@ type Provider struct { Scopes []string MapUser func(userData, []byte) store.User - avatarProxy *avatar.Proxy + avatarProxy *AvatarProxy conf *oauth2.Config } @@ -43,7 +42,7 @@ type Params struct { Csecret string SessionStore sessions.Store RemarkURL string - AvatarProxy *avatar.Proxy + AvatarProxy *AvatarProxy } type userData map[string]interface{} @@ -100,7 +99,7 @@ func (p Provider) loginHandler(w http.ResponseWriter, r *http.Request) { log.Printf("[DEBUG] login, %+v", session.Values) if err := session.Save(r, w); err != nil { - common.SendErrorJSON(w, r, http.StatusInternalServerError, err, "failed to save state") + rest.SendErrorJSON(w, r, http.StatusInternalServerError, err, "failed to save state") return } @@ -116,7 +115,7 @@ func (p Provider) authHandler(w http.ResponseWriter, r *http.Request) { session, err := p.Get(r, "remark") if err != nil { - common.SendErrorJSON(w, r, http.StatusInternalServerError, err, "failed to get session") + rest.SendErrorJSON(w, r, http.StatusInternalServerError, err, "failed to get session") return } @@ -135,14 +134,14 @@ func (p Provider) authHandler(w http.ResponseWriter, r *http.Request) { log.Printf("[DEBUG] auth, %+v", session.Values) tok, err := p.conf.Exchange(context.Background(), r.URL.Query().Get("code")) if err != nil { - common.SendErrorJSON(w, r, http.StatusInternalServerError, err, "exchange failed") + rest.SendErrorJSON(w, r, http.StatusInternalServerError, err, "exchange failed") return } client := p.conf.Client(context.Background(), tok) uinfo, err := client.Get(p.InfoURL) if err != nil { - common.SendErrorJSON(w, r, http.StatusBadRequest, err, fmt.Sprintf("failed to get client info via %s", p.InfoURL)) + rest.SendErrorJSON(w, r, http.StatusBadRequest, err, fmt.Sprintf("failed to get client info via %s", p.InfoURL)) return } @@ -154,13 +153,13 @@ func (p Provider) authHandler(w http.ResponseWriter, r *http.Request) { data, err := ioutil.ReadAll(uinfo.Body) if err != nil { - common.SendErrorJSON(w, r, http.StatusInternalServerError, err, "failed to read user info") + rest.SendErrorJSON(w, r, http.StatusInternalServerError, err, "failed to read user info") return } jData := map[string]interface{}{} if e := json.Unmarshal(data, &jData); e != nil { - common.SendErrorJSON(w, r, http.StatusInternalServerError, err, "failed to unmarshal user info") + rest.SendErrorJSON(w, r, http.StatusInternalServerError, err, "failed to unmarshal user info") return } log.Printf("[DEBUG] got raw user info %+v", jData) @@ -176,7 +175,7 @@ func (p Provider) authHandler(w http.ResponseWriter, r *http.Request) { session.Values["uinfo"] = u if err = session.Save(r, w); err != nil { - common.SendErrorJSON(w, r, http.StatusInternalServerError, err, "failed to save user info") + rest.SendErrorJSON(w, r, http.StatusInternalServerError, err, "failed to save user info") return } @@ -195,7 +194,7 @@ func (p Provider) authHandler(w http.ResponseWriter, r *http.Request) { func (p Provider) LogoutHandler(w http.ResponseWriter, r *http.Request) { session, err := p.Get(r, "remark") if err != nil { - common.SendErrorJSON(w, r, http.StatusBadRequest, err, "failed to get session") + rest.SendErrorJSON(w, r, http.StatusBadRequest, err, "failed to get session") return } @@ -208,7 +207,7 @@ func (p Provider) LogoutHandler(w http.ResponseWriter, r *http.Request) { delete(session.Values, "state") if err = session.Save(r, w); err != nil { - common.SendErrorJSON(w, r, http.StatusInternalServerError, err, "failed to reset user info") + rest.SendErrorJSON(w, r, http.StatusInternalServerError, err, "failed to reset user info") return } log.Printf("[DEBUG] logout, %+v", session.Values) diff --git a/app/rest/common/cache.go b/app/rest/cache.go similarity index 98% rename from app/rest/common/cache.go rename to app/rest/cache.go index 58446f1a..a85227e0 100644 --- a/app/rest/common/cache.go +++ b/app/rest/cache.go @@ -1,4 +1,4 @@ -package common +package rest import ( "log" diff --git a/app/rest/common/http_errors.go b/app/rest/http_errors.go similarity index 82% rename from app/rest/common/http_errors.go rename to app/rest/http_errors.go index 950efb00..683d0d06 100644 --- a/app/rest/common/http_errors.go +++ b/app/rest/http_errors.go @@ -1,4 +1,4 @@ -package common +package rest import ( "fmt" @@ -18,12 +18,6 @@ func SendErrorJSON(w http.ResponseWriter, r *http.Request, code int, err error, render.JSON(w, r, map[string]interface{}{"error": err.Error(), "details": details}) } -// SendErrorText with simple text body and responds with error code -func SendErrorText(w http.ResponseWriter, r *http.Request, code int, text string) { - render.Status(r, code) - render.PlainText(w, r, text) -} - func logDetails(r *http.Request, code int, err error, details string) { uinfoStr := "" if user, е := GetUserInfo(r); е == nil { diff --git a/app/rest/admin.go b/app/rest/server/admin.go similarity index 86% rename from app/rest/admin.go rename to app/rest/server/admin.go index fb1f0f67..bd3a0134 100644 --- a/app/rest/admin.go +++ b/app/rest/server/admin.go @@ -1,4 +1,4 @@ -package rest +package server import ( "compress/gzip" @@ -12,7 +12,7 @@ import ( "github.com/go-chi/render" "github.com/umputun/remark/app/migrator" - "github.com/umputun/remark/app/rest/common" + "github.com/umputun/remark/app/rest" "github.com/umputun/remark/app/store" ) @@ -21,7 +21,7 @@ type admin struct { dataService store.Service exporter migrator.Exporter importer migrator.Importer - cache common.LoadingCache + cache rest.LoadingCache } func (a *admin) routes(middlewares ...func(http.Handler) http.Handler) chi.Router { @@ -45,7 +45,7 @@ func (a *admin) deleteCommentCtrl(w http.ResponseWriter, r *http.Request) { err := a.dataService.Delete(locator, id) if err != nil { - common.SendErrorJSON(w, r, http.StatusInternalServerError, err, "can't delete comment") + rest.SendErrorJSON(w, r, http.StatusInternalServerError, err, "can't delete comment") return } a.cache.Flush() @@ -60,7 +60,7 @@ func (a *admin) setBlockCtrl(w http.ResponseWriter, r *http.Request) { blockStatus := r.URL.Query().Get("block") == "1" if err := a.dataService.SetBlock(siteID, userID, blockStatus); err != nil { - common.SendErrorJSON(w, r, http.StatusBadRequest, err, "can't set blocking status") + rest.SendErrorJSON(w, r, http.StatusBadRequest, err, "can't set blocking status") return } a.cache.Flush() @@ -72,7 +72,7 @@ func (a *admin) blockedUsersCtrl(w http.ResponseWriter, r *http.Request) { siteID := r.URL.Query().Get("site") users, err := a.dataService.Blocked(siteID) if err != nil { - common.SendErrorJSON(w, r, http.StatusBadRequest, err, "can't get blocked users") + rest.SendErrorJSON(w, r, http.StatusBadRequest, err, "can't get blocked users") return } render.JSON(w, r, users) @@ -86,7 +86,7 @@ func (a *admin) setPinCtrl(w http.ResponseWriter, r *http.Request) { pinStatus := r.URL.Query().Get("pin") == "1" if err := a.dataService.SetPin(locator, commentID, pinStatus); err != nil { - common.SendErrorJSON(w, r, http.StatusBadRequest, err, "can't set pin status") + rest.SendErrorJSON(w, r, http.StatusBadRequest, err, "can't set pin status") return } a.cache.Flush() @@ -107,7 +107,7 @@ func (a *admin) exportCtrl(w http.ResponseWriter, r *http.Request) { } if err := a.exporter.Export(writer, siteID); err != nil { - common.SendErrorJSON(w, r, http.StatusInternalServerError, err, "export failed") + rest.SendErrorJSON(w, r, http.StatusInternalServerError, err, "export failed") } } @@ -116,7 +116,7 @@ func (a *admin) exportCtrl(w http.ResponseWriter, r *http.Request) { func (a *admin) importCtrl(w http.ResponseWriter, r *http.Request) { siteID := r.URL.Query().Get("site") if err := a.importer.Import(r.Body, siteID); err != nil { - common.SendErrorJSON(w, r, http.StatusBadRequest, err, "import failed") + rest.SendErrorJSON(w, r, http.StatusBadRequest, err, "import failed") } a.cache.Flush() } diff --git a/app/rest/admin_test.go b/app/rest/server/admin_test.go similarity index 99% rename from app/rest/admin_test.go rename to app/rest/server/admin_test.go index 28cf3033..73106d11 100644 --- a/app/rest/admin_test.go +++ b/app/rest/server/admin_test.go @@ -1,4 +1,4 @@ -package rest +package server import ( "encoding/json" diff --git a/app/rest/middleware.go b/app/rest/server/middleware.go similarity index 97% rename from app/rest/middleware.go rename to app/rest/server/middleware.go index 230a7866..67078fa4 100644 --- a/app/rest/middleware.go +++ b/app/rest/server/middleware.go @@ -1,4 +1,4 @@ -package rest +package server import ( "bytes" @@ -14,8 +14,7 @@ import ( "time" "github.com/go-chi/chi/middleware" - - "github.com/umputun/remark/app/rest/common" + "github.com/umputun/remark/app/rest" ) // JSON is a map alias, just for convenience @@ -123,7 +122,7 @@ func Logger(flags ...LoggerFlag) func(http.Handler) http.Handler { } if inFlags(LogUser) { - u, err := common.GetUserInfo(r) + u, err := rest.GetUserInfo(r) if err == nil && u.Name != "" { user = fmt.Sprintf(" - %s %q", u.ID, u.Name) } diff --git a/app/rest/server.go b/app/rest/server/rest.go similarity index 79% rename from app/rest/server.go rename to app/rest/server/rest.go index d7bdaaa7..27cf057e 100644 --- a/app/rest/server.go +++ b/app/rest/server/rest.go @@ -1,4 +1,4 @@ -package rest +package server import ( "bytes" @@ -22,21 +22,21 @@ import ( "github.com/umputun/remark/app/migrator" "github.com/umputun/remark/app/notifier" + "github.com/umputun/remark/app/rest" "github.com/umputun/remark/app/rest/auth" - "github.com/umputun/remark/app/rest/common" "github.com/umputun/remark/app/rest/format" "github.com/umputun/remark/app/store" ) -// Server is a rest access server -type Server struct { +// Rest is a rest access server +type Rest struct { Version string DevMode bool DataService store.Service Authenticator auth.Authenticator Exporter migrator.Exporter - Cache common.LoadingCache + Cache rest.LoadingCache Notifier notifier.Interface httpServer *http.Server @@ -44,7 +44,7 @@ type Server struct { } // Run the lister and request's router, activate rest server -func (s *Server) Run(port int) { +func (s *Rest) Run(port int) { log.Print("[INFO] activate rest server") // add auth.Developer flag if dev mode is active @@ -122,17 +122,17 @@ func (s *Server) Run(port int) { } // POST /comment - adds comment, resets all immutable fields -func (s *Server) createCommentCtrl(w http.ResponseWriter, r *http.Request) { +func (s *Rest) createCommentCtrl(w http.ResponseWriter, r *http.Request) { comment := store.Comment{} if err := render.DecodeJSON(r.Body, &comment); err != nil { - common.SendErrorJSON(w, r, http.StatusBadRequest, err, "can't bind comment") + rest.SendErrorJSON(w, r, http.StatusBadRequest, err, "can't bind comment") return } - user, err := common.GetUserInfo(r) + user, err := rest.GetUserInfo(r) if err != nil { // this not suppose to happen (handled by Auth), just dbl-check - common.SendErrorJSON(w, r, http.StatusUnauthorized, err, "can't get user info") + rest.SendErrorJSON(w, r, http.StatusUnauthorized, err, "can't get user info") return } @@ -156,13 +156,13 @@ func (s *Server) createCommentCtrl(w http.ResponseWriter, r *http.Request) { // check if user blocked if s.mod.checkBlocked(comment.Locator.SiteID, comment.User) { - common.SendErrorJSON(w, r, http.StatusForbidden, errors.New("rejected"), "user blocked") + rest.SendErrorJSON(w, r, http.StatusForbidden, errors.New("rejected"), "user blocked") return } id, err := s.DataService.Create(comment) if err != nil { - common.SendErrorJSON(w, r, http.StatusInternalServerError, err, "can't save comment") + rest.SendErrorJSON(w, r, http.StatusInternalServerError, err, "can't save comment") return } @@ -176,7 +176,7 @@ func (s *Server) createCommentCtrl(w http.ResponseWriter, r *http.Request) { } // PUT /comment/{id}?site=siteID&url=post-url - update comment -func (s *Server) updateCommentCtrl(w http.ResponseWriter, r *http.Request) { +func (s *Rest) updateCommentCtrl(w http.ResponseWriter, r *http.Request) { edit := struct { Text string @@ -184,13 +184,13 @@ func (s *Server) updateCommentCtrl(w http.ResponseWriter, r *http.Request) { }{} if err := render.DecodeJSON(r.Body, &edit); err != nil { - common.SendErrorJSON(w, r, http.StatusBadRequest, err, "can't bind comment") + rest.SendErrorJSON(w, r, http.StatusBadRequest, err, "can't bind comment") return } - user, err := common.GetUserInfo(r) + user, err := rest.GetUserInfo(r) if err != nil { // this not suppose to happen (handled by Auth), just dbl-check - common.SendErrorJSON(w, r, http.StatusUnauthorized, err, "can't get user info") + rest.SendErrorJSON(w, r, http.StatusUnauthorized, err, "can't get user info") return } locator := store.Locator{SiteID: r.URL.Query().Get("site"), URL: r.URL.Query().Get("url")} @@ -203,18 +203,18 @@ func (s *Server) updateCommentCtrl(w http.ResponseWriter, r *http.Request) { var currComment store.Comment if currComment, err = s.DataService.Get(locator, id); err != nil { - common.SendErrorJSON(w, r, http.StatusBadRequest, err, "can't find comment") + rest.SendErrorJSON(w, r, http.StatusBadRequest, err, "can't find comment") return } if currComment.User.ID != user.ID { - common.SendErrorJSON(w, r, http.StatusForbidden, errors.New("rejected"), "can not edit comments for other users") + rest.SendErrorJSON(w, r, http.StatusForbidden, errors.New("rejected"), "can not edit comments for other users") return } res, err := s.DataService.EditComment(locator, id, edit.Text, store.Edit{Summary: edit.Summary}) if err != nil { - common.SendErrorJSON(w, r, http.StatusBadRequest, err, "can't update comment") + rest.SendErrorJSON(w, r, http.StatusBadRequest, err, "can't update comment") return } @@ -224,7 +224,7 @@ func (s *Server) updateCommentCtrl(w http.ResponseWriter, r *http.Request) { // GET /find?site=siteID&url=post-url&format=[tree|plain]&sort=[+/-time|+/-score] // find comments for given post. Returns in tree or plain formats, sorted -func (s *Server) findCommentsCtrl(w http.ResponseWriter, r *http.Request) { +func (s *Rest) findCommentsCtrl(w http.ResponseWriter, r *http.Request) { locator := store.Locator{SiteID: r.URL.Query().Get("site"), URL: r.URL.Query().Get("url")} log.Printf("[DEBUG] get comments for %+v", locator) @@ -245,14 +245,14 @@ func (s *Server) findCommentsCtrl(w http.ResponseWriter, r *http.Request) { }) if err != nil { - common.SendErrorJSON(w, r, http.StatusBadRequest, err, "can't find comments") + rest.SendErrorJSON(w, r, http.StatusBadRequest, err, "can't find comments") return } renderJSONFromBytes(w, r, data) } // GET /last/{max}?site=siteID - last comments for the siteID, across all posts, sorted by time -func (s *Server) lastCommentsCtrl(w http.ResponseWriter, r *http.Request) { +func (s *Rest) lastCommentsCtrl(w http.ResponseWriter, r *http.Request) { log.Printf("[DEBUG] get last comments for %s", r.URL.Query().Get("site")) @@ -271,14 +271,14 @@ func (s *Server) lastCommentsCtrl(w http.ResponseWriter, r *http.Request) { }) if err != nil { - common.SendErrorJSON(w, r, http.StatusInternalServerError, err, "can't get last comments") + rest.SendErrorJSON(w, r, http.StatusInternalServerError, err, "can't get last comments") return } renderJSONFromBytes(w, r, data) } // GET /id/{id}?site=siteID&url=post-url - gets a comment by id -func (s *Server) commentByIDCtrl(w http.ResponseWriter, r *http.Request) { +func (s *Rest) commentByIDCtrl(w http.ResponseWriter, r *http.Request) { id := chi.URLParam(r, "id") siteID := r.URL.Query().Get("site") @@ -288,7 +288,7 @@ func (s *Server) commentByIDCtrl(w http.ResponseWriter, r *http.Request) { comment, err := s.DataService.Get(store.Locator{SiteID: siteID, URL: url}, id) if err != nil { - common.SendErrorJSON(w, r, http.StatusBadRequest, err, "can't get comment by id") + rest.SendErrorJSON(w, r, http.StatusBadRequest, err, "can't get comment by id") return } comment = s.mod.maskBlockedUsers([]store.Comment{comment})[0] @@ -297,7 +297,7 @@ func (s *Server) commentByIDCtrl(w http.ResponseWriter, r *http.Request) { } // GET /comments?site=siteID&user=id - returns comments for given userID -func (s *Server) findUserCommentsCtrl(w http.ResponseWriter, r *http.Request) { +func (s *Rest) findUserCommentsCtrl(w http.ResponseWriter, r *http.Request) { userID := r.URL.Query().Get("user") siteID := r.URL.Query().Get("site") @@ -320,14 +320,14 @@ func (s *Server) findUserCommentsCtrl(w http.ResponseWriter, r *http.Request) { }) if err != nil { - common.SendErrorJSON(w, r, http.StatusBadRequest, err, "can't get comment by user id") + rest.SendErrorJSON(w, r, http.StatusBadRequest, err, "can't get comment by user id") return } renderJSONFromBytes(w, r, data) } // GET /config?site=siteID - returns configuration -func (s *Server) configCtrl(w http.ResponseWriter, r *http.Request) { +func (s *Rest) configCtrl(w http.ResponseWriter, r *http.Request) { type config struct { Version string `json:"version"` EditDuration int `json:"edit_duration"` @@ -350,28 +350,28 @@ func (s *Server) configCtrl(w http.ResponseWriter, r *http.Request) { } // GET /user - returns user info -func (s *Server) userInfoCtrl(w http.ResponseWriter, r *http.Request) { - user, err := common.GetUserInfo(r) +func (s *Rest) userInfoCtrl(w http.ResponseWriter, r *http.Request) { + user, err := rest.GetUserInfo(r) if err != nil { - common.SendErrorJSON(w, r, http.StatusUnauthorized, err, "can't get user info") + rest.SendErrorJSON(w, r, http.StatusUnauthorized, err, "can't get user info") return } render.JSON(w, r, user) } // GET /count?site=siteID&url=post-url - get number of comments for given post -func (s *Server) countCtrl(w http.ResponseWriter, r *http.Request) { +func (s *Rest) countCtrl(w http.ResponseWriter, r *http.Request) { locator := store.Locator{SiteID: r.URL.Query().Get("site"), URL: r.URL.Query().Get("url")} count, err := s.DataService.Count(locator) if err != nil { - common.SendErrorJSON(w, r, http.StatusBadRequest, err, "can't get count") + rest.SendErrorJSON(w, r, http.StatusBadRequest, err, "can't get count") return } render.JSON(w, r, JSON{"count": count, "locator": locator}) } // GET /list?site=siteID - list posts with comments -func (s *Server) listCtrl(w http.ResponseWriter, r *http.Request) { +func (s *Rest) listCtrl(w http.ResponseWriter, r *http.Request) { siteID := r.URL.Query().Get("site") data, err := s.Cache.Get(r.URL.String(), 8*time.Hour, func() ([]byte, error) { @@ -383,18 +383,18 @@ func (s *Server) listCtrl(w http.ResponseWriter, r *http.Request) { }) if err != nil { - common.SendErrorJSON(w, r, http.StatusBadRequest, err, "can't get list of comments for "+siteID) + rest.SendErrorJSON(w, r, http.StatusBadRequest, err, "can't get list of comments for "+siteID) return } renderJSONFromBytes(w, r, data) } // PUT /vote/{id}?site=siteID&url=post-url&vote=1 - vote for/against comment -func (s *Server) voteCtrl(w http.ResponseWriter, r *http.Request) { +func (s *Rest) voteCtrl(w http.ResponseWriter, r *http.Request) { - user, err := common.GetUserInfo(r) + user, err := rest.GetUserInfo(r) if err != nil { - common.SendErrorJSON(w, r, http.StatusUnauthorized, err, "can't get user info") + rest.SendErrorJSON(w, r, http.StatusUnauthorized, err, "can't get user info") return } locator := store.Locator{SiteID: r.URL.Query().Get("site"), URL: r.URL.Query().Get("url")} @@ -405,7 +405,7 @@ func (s *Server) voteCtrl(w http.ResponseWriter, r *http.Request) { comment, err := s.DataService.Vote(locator, id, user.ID, vote) if err != nil { - common.SendErrorJSON(w, r, http.StatusBadRequest, err, "can't vote for comment") + rest.SendErrorJSON(w, r, http.StatusBadRequest, err, "can't vote for comment") return } s.Cache.Flush() @@ -413,10 +413,10 @@ func (s *Server) voteCtrl(w http.ResponseWriter, r *http.Request) { } // PUT /notify?site=siteID&url=post-url&action=1 - subscribe/unsubscribe to notification -func (s *Server) notifyActionCtrl(w http.ResponseWriter, r *http.Request) { - user, err := common.GetUserInfo(r) +func (s *Rest) notifyActionCtrl(w http.ResponseWriter, r *http.Request) { + user, err := rest.GetUserInfo(r) if err != nil { - common.SendErrorJSON(w, r, http.StatusUnauthorized, err, "can't get user info") + rest.SendErrorJSON(w, r, http.StatusUnauthorized, err, "can't get user info") return } locator := store.Locator{SiteID: r.URL.Query().Get("site"), URL: r.URL.Query().Get("url")} @@ -430,17 +430,17 @@ func (s *Server) notifyActionCtrl(w http.ResponseWriter, r *http.Request) { action = "unsubscribe" } if err != nil { - common.SendErrorJSON(w, r, http.StatusBadRequest, err, "can't subscribe/unsubscribe for notifications") + rest.SendErrorJSON(w, r, http.StatusBadRequest, err, "can't subscribe/unsubscribe for notifications") return } render.JSON(w, r, JSON{"locator": locator, "user": user.ID, "action": action}) } // GET /notify?site=siteID&url=post-url - get notification status -func (s *Server) notifyStatusCtrl(w http.ResponseWriter, r *http.Request) { - user, err := common.GetUserInfo(r) +func (s *Rest) notifyStatusCtrl(w http.ResponseWriter, r *http.Request) { + user, err := rest.GetUserInfo(r) if err != nil { - common.SendErrorJSON(w, r, http.StatusUnauthorized, err, "can't get user info") + rest.SendErrorJSON(w, r, http.StatusUnauthorized, err, "can't get user info") return } locator := store.Locator{SiteID: r.URL.Query().Get("site"), URL: r.URL.Query().Get("url")} @@ -452,7 +452,7 @@ func (s *Server) notifyStatusCtrl(w http.ResponseWriter, r *http.Request) { } // serves static files from /web -func (s *Server) addFileServer(r chi.Router, path string, root http.FileSystem) { +func (s *Rest) addFileServer(r chi.Router, path string, root http.FileSystem) { log.Printf("[INFO] run file server for %s", root) fs := http.StripPrefix(path, http.FileServer(root)) if path != "/" && path[len(path)-1] != '/' { @@ -475,7 +475,7 @@ func (s *Server) addFileServer(r chi.Router, path string, root http.FileSystem) func renderJSONWithHTML(w http.ResponseWriter, r *http.Request, v interface{}) { data, err := encodeJSONWithHTML(v) if err != nil { - common.SendErrorJSON(w, r, http.StatusInternalServerError, err, "can't render json response") + rest.SendErrorJSON(w, r, http.StatusInternalServerError, err, "can't render json response") return } renderJSONFromBytes(w, r, data) diff --git a/app/rest/server_test.go b/app/rest/server/rest_test.go similarity index 98% rename from app/rest/server_test.go rename to app/rest/server/rest_test.go index ee4716b2..13a58234 100644 --- a/app/rest/server_test.go +++ b/app/rest/server/rest_test.go @@ -1,4 +1,4 @@ -package rest +package server import ( "bytes" @@ -19,7 +19,6 @@ import ( "github.com/umputun/remark/app/migrator" "github.com/umputun/remark/app/notifier" "github.com/umputun/remark/app/rest/auth" - "github.com/umputun/remark/app/rest/avatar" "github.com/umputun/remark/app/store" ) @@ -348,7 +347,7 @@ func prep(t *testing.T) (srv *Server, port int) { DevMode: true, Authenticator: auth.Authenticator{ Providers: nil, - AvatarProxy: &avatar.Proxy{StorePath: "/tmp", RoutePath: "/api/v1/avatar"}, + AvatarProxy: &auth.AvatarProxy{StorePath: "/tmp", RoutePath: "/api/v1/avatar"}, }, Exporter: &migrator.Remark{DataStore: dataStore}, Cache: &mockCache{}, diff --git a/app/rest/common/user.go b/app/rest/user.go similarity index 97% rename from app/rest/common/user.go rename to app/rest/user.go index aa9222e1..cb321838 100644 --- a/app/rest/common/user.go +++ b/app/rest/user.go @@ -1,4 +1,4 @@ -package common +package rest import ( "errors"