refactor rest to less packages
This commit is contained in:
+6
-7
@@ -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 != "" {
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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))
|
||||
@@ -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")
|
||||
+12
-13
@@ -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)
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
package common
|
||||
package rest
|
||||
|
||||
import (
|
||||
"log"
|
||||
@@ -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 {
|
||||
@@ -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()
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
package rest
|
||||
package server
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
@@ -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)
|
||||
}
|
||||
@@ -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)
|
||||
@@ -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{},
|
||||
@@ -1,4 +1,4 @@
|
||||
package common
|
||||
package rest
|
||||
|
||||
import (
|
||||
"errors"
|
||||
Reference in New Issue
Block a user