isolate cache, simplify list of auth providers
This commit is contained in:
+26
-26
@@ -14,6 +14,7 @@ import (
|
||||
"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/common"
|
||||
"github.com/umputun/remark/app/store"
|
||||
)
|
||||
|
||||
@@ -39,6 +40,8 @@ var opts struct {
|
||||
GithubCSEC string `long:"github-csec" env:"REMARK_GITHUB_CSEC" description:"Github OAuth client secret"`
|
||||
FacebookCID string `long:"facebook-cid" env:"REMARK_FACEBOOK_CID" description:"Facebook OAuth client ID"`
|
||||
FacebookCSEC string `long:"facebook-csec" env:"REMARK_FACEBOOK_CSEC" description:"Facebook OAuth client secret"`
|
||||
|
||||
Port int `long:"port" env:"REMARK_PORT" default:"8080" description:"port"`
|
||||
} `command:"server" description:"run server"`
|
||||
|
||||
ImportCommand struct {
|
||||
@@ -80,36 +83,33 @@ func main() {
|
||||
return
|
||||
}
|
||||
|
||||
srvOpts := opts.ServerCommand
|
||||
dataService := store.Service{Interface: dataStore, EditDuration: 5 * time.Minute}
|
||||
sessionStore := sessions.NewFilesystemStore(opts.ServerCommand.SessionStore, []byte(opts.ServerCommand.StoreKey))
|
||||
sessionStore := sessions.NewFilesystemStore(srvOpts.SessionStore, []byte(srvOpts.StoreKey))
|
||||
sessionStore.Options.HttpOnly = true
|
||||
exporter := migrator.Remark{DataStore: dataStore}
|
||||
|
||||
authProviders := []auth.Provider{
|
||||
auth.NewGoogle(auth.Params{
|
||||
Cid: srvOpts.GoogleCID, Csecret: srvOpts.GoogleCSEC, SessionStore: sessionStore, RemarkURL: opts.RemarkURL,
|
||||
}),
|
||||
auth.NewGithub(auth.Params{
|
||||
Cid: srvOpts.GithubCID, Csecret: srvOpts.GithubCSEC, SessionStore: sessionStore, RemarkURL: opts.RemarkURL,
|
||||
}),
|
||||
auth.NewFacebook(auth.Params{
|
||||
Cid: srvOpts.FacebookCID, Csecret: srvOpts.FacebookCSEC, SessionStore: sessionStore, RemarkURL: opts.RemarkURL,
|
||||
}),
|
||||
}
|
||||
|
||||
srv := rest.Server{
|
||||
Version: revision,
|
||||
DataService: dataService,
|
||||
SessionStore: sessionStore,
|
||||
Admins: opts.Admins,
|
||||
DevMode: opts.DevMode,
|
||||
Exporter: &exporter,
|
||||
AuthGoogle: auth.NewGoogle(auth.Params{
|
||||
Cid: opts.ServerCommand.GoogleCID,
|
||||
Csecret: opts.ServerCommand.GoogleCSEC,
|
||||
SessionStore: sessionStore,
|
||||
RemarkURL: opts.RemarkURL,
|
||||
}),
|
||||
AuthGithub: auth.NewGithub(auth.Params{
|
||||
Cid: opts.ServerCommand.GithubCID,
|
||||
Csecret: opts.ServerCommand.GithubCSEC,
|
||||
SessionStore: sessionStore,
|
||||
RemarkURL: opts.RemarkURL,
|
||||
}),
|
||||
AuthFacebook: auth.NewFacebook(auth.Params{
|
||||
Cid: opts.ServerCommand.FacebookCID,
|
||||
Csecret: opts.ServerCommand.FacebookCSEC,
|
||||
SessionStore: sessionStore,
|
||||
RemarkURL: opts.RemarkURL,
|
||||
}),
|
||||
Version: revision,
|
||||
DataService: dataService,
|
||||
SessionStore: sessionStore,
|
||||
Admins: opts.Admins,
|
||||
DevMode: opts.DevMode,
|
||||
Exporter: &exporter,
|
||||
Cache: common.NewLoadingCache(4*time.Hour, 15*time.Minute),
|
||||
AuthProviders: authProviders,
|
||||
}
|
||||
|
||||
if opts.DevMode {
|
||||
@@ -126,7 +126,7 @@ func main() {
|
||||
}.Do()
|
||||
}
|
||||
|
||||
srv.Run(8080)
|
||||
srv.Run(srvOpts.Port)
|
||||
}
|
||||
|
||||
// makeBoltStore creates store for all sites
|
||||
|
||||
+5
-5
@@ -22,7 +22,7 @@ type admin struct {
|
||||
dataService store.Service
|
||||
exporter migrator.Exporter
|
||||
importer migrator.Importer
|
||||
respCache *loadingCache
|
||||
cache common.LoadingCache
|
||||
}
|
||||
|
||||
func (a *admin) routes() chi.Router {
|
||||
@@ -48,7 +48,7 @@ func (a *admin) deleteCommentCtrl(w http.ResponseWriter, r *http.Request) {
|
||||
common.SendErrorJSON(w, r, http.StatusInternalServerError, err, "can't delete comment")
|
||||
return
|
||||
}
|
||||
a.respCache.flush()
|
||||
a.cache.Flush()
|
||||
render.Status(r, http.StatusOK)
|
||||
render.JSON(w, r, JSON{"id": id, "loc": locator})
|
||||
}
|
||||
@@ -63,7 +63,7 @@ func (a *admin) setBlockCtrl(w http.ResponseWriter, r *http.Request) {
|
||||
common.SendErrorJSON(w, r, http.StatusBadRequest, err, "can't set blocking status")
|
||||
return
|
||||
}
|
||||
a.respCache.flush()
|
||||
a.cache.Flush()
|
||||
render.JSON(w, r, JSON{"user_id": userID, "site_id": siteID, "block": blockStatus})
|
||||
}
|
||||
|
||||
@@ -78,7 +78,7 @@ func (a *admin) setPinCtrl(w http.ResponseWriter, r *http.Request) {
|
||||
common.SendErrorJSON(w, r, http.StatusBadRequest, err, "can't set pin status")
|
||||
return
|
||||
}
|
||||
a.respCache.flush()
|
||||
a.cache.Flush()
|
||||
render.JSON(w, r, JSON{"id": commentID, "loc": locator, "pin": pinStatus})
|
||||
}
|
||||
|
||||
@@ -107,7 +107,7 @@ func (a *admin) importCtrl(w http.ResponseWriter, r *http.Request) {
|
||||
if err := a.importer.Import(r.Body, siteID); err != nil {
|
||||
common.SendErrorJSON(w, r, http.StatusBadRequest, err, "import failed")
|
||||
}
|
||||
a.respCache.flush()
|
||||
a.cache.Flush()
|
||||
}
|
||||
|
||||
func (a *admin) checkBlocked(siteID string, user store.User) bool {
|
||||
|
||||
@@ -43,7 +43,7 @@ type Params struct {
|
||||
}
|
||||
|
||||
// newProvider makes auth for given provider
|
||||
func initProvider(p Params, provider Provider) *Provider {
|
||||
func initProvider(p Params, provider Provider) Provider {
|
||||
log.Printf("[INFO] create %s auth, id=%s, redir: %s", provider.Name, p.Cid, provider.RedirectURL)
|
||||
|
||||
conf := oauth2.Config{
|
||||
@@ -56,7 +56,7 @@ func initProvider(p Params, provider Provider) *Provider {
|
||||
|
||||
provider.conf = &conf
|
||||
provider.Store = p.SessionStore
|
||||
return &provider
|
||||
return provider
|
||||
}
|
||||
|
||||
// Routes returns auth routes for given provider
|
||||
|
||||
@@ -12,7 +12,7 @@ import (
|
||||
)
|
||||
|
||||
// NewGoogle makes google oauth2 provider
|
||||
func NewGoogle(p Params) *Provider {
|
||||
func NewGoogle(p Params) Provider {
|
||||
return initProvider(p, Provider{
|
||||
Name: "google",
|
||||
Endpoint: google.Endpoint,
|
||||
@@ -37,7 +37,7 @@ func NewGoogle(p Params) *Provider {
|
||||
}
|
||||
|
||||
// NewGithub makes github oauth2 provider
|
||||
func NewGithub(p Params) *Provider {
|
||||
func NewGithub(p Params) Provider {
|
||||
return initProvider(p, Provider{
|
||||
Name: "github",
|
||||
Endpoint: github.Endpoint,
|
||||
@@ -62,7 +62,7 @@ func NewGithub(p Params) *Provider {
|
||||
}
|
||||
|
||||
// NewFacebook makes facebook oauth2 provider
|
||||
func NewFacebook(p Params) *Provider {
|
||||
func NewFacebook(p Params) Provider {
|
||||
return initProvider(p, Provider{
|
||||
Name: "facebook",
|
||||
Endpoint: facebook.Endpoint,
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
package rest
|
||||
package common
|
||||
|
||||
import (
|
||||
"log"
|
||||
@@ -7,15 +7,23 @@ import (
|
||||
cache "github.com/patrickmn/go-cache"
|
||||
)
|
||||
|
||||
// LoadingCache defines interface for caching
|
||||
type LoadingCache interface {
|
||||
Get(key string, ttl time.Duration, fn func() ([]byte, error)) (data []byte, err error)
|
||||
Flush()
|
||||
}
|
||||
|
||||
// loadingCache implements LoadingCache interface on top of cache.Cache
|
||||
type loadingCache struct {
|
||||
bytesCache *cache.Cache
|
||||
}
|
||||
|
||||
func newLoadingCache(defaultExpiration, cleanupInterval time.Duration) *loadingCache {
|
||||
// NewLoadingCache makes loadingCache implementation
|
||||
func NewLoadingCache(defaultExpiration, cleanupInterval time.Duration) LoadingCache {
|
||||
return &loadingCache{bytesCache: cache.New(defaultExpiration, cleanupInterval)}
|
||||
}
|
||||
|
||||
func (lc *loadingCache) get(key string, ttl time.Duration, fn func() ([]byte, error)) (data []byte, err error) {
|
||||
func (lc *loadingCache) Get(key string, ttl time.Duration, fn func() ([]byte, error)) (data []byte, err error) {
|
||||
if b, ok := lc.bytesCache.Get(key); ok {
|
||||
log.Printf("[DEBUG] cache hit %s", key)
|
||||
return b.([]byte), nil
|
||||
@@ -29,6 +37,6 @@ func (lc *loadingCache) get(key string, ttl time.Duration, fn func() ([]byte, er
|
||||
return data, nil
|
||||
}
|
||||
|
||||
func (lc *loadingCache) flush() {
|
||||
func (lc *loadingCache) Flush() {
|
||||
lc.bytesCache.Flush()
|
||||
}
|
||||
+21
-26
@@ -17,30 +17,28 @@ import (
|
||||
"github.com/gorilla/context"
|
||||
"github.com/gorilla/sessions"
|
||||
"github.com/pkg/errors"
|
||||
"gopkg.in/russross/blackfriday.v2"
|
||||
|
||||
"github.com/umputun/remark/app/migrator"
|
||||
"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"
|
||||
"gopkg.in/russross/blackfriday.v2"
|
||||
)
|
||||
|
||||
// Server is a rest access server
|
||||
type Server struct {
|
||||
Version string
|
||||
DataService store.Service
|
||||
Admins []string
|
||||
AuthGoogle *auth.Provider
|
||||
AuthGithub *auth.Provider
|
||||
AuthFacebook *auth.Provider
|
||||
SessionStore *sessions.FilesystemStore
|
||||
Exporter migrator.Exporter
|
||||
DevMode bool
|
||||
Version string
|
||||
DataService store.Service
|
||||
Admins []string
|
||||
AuthProviders []auth.Provider
|
||||
SessionStore *sessions.FilesystemStore
|
||||
Exporter migrator.Exporter
|
||||
Cache common.LoadingCache
|
||||
DevMode bool
|
||||
|
||||
httpServer *http.Server
|
||||
mod admin
|
||||
respCache *loadingCache
|
||||
}
|
||||
|
||||
// Run the lister and request's router, activate rest server
|
||||
@@ -60,9 +58,6 @@ func (s *Server) Run(port int) {
|
||||
log.Printf("[DEBUG] admins %+v", s.Admins)
|
||||
}
|
||||
|
||||
// cache for responses. Flushes completely on any modification
|
||||
s.respCache = newLoadingCache(4*time.Hour, 15*time.Minute)
|
||||
|
||||
router := chi.NewRouter()
|
||||
router.Use(middleware.RealIP, Recoverer)
|
||||
router.Use(middleware.Throttle(1000), middleware.Timeout(60*time.Second))
|
||||
@@ -73,10 +68,10 @@ func (s *Server) Run(port int) {
|
||||
|
||||
// auth routes for all providers
|
||||
router.Route("/auth", func(r chi.Router) {
|
||||
r.Mount("/google", s.AuthGoogle.Routes())
|
||||
r.Mount("/github", s.AuthGithub.Routes())
|
||||
r.Mount("/facebook", s.AuthFacebook.Routes())
|
||||
r.Get("/logout", s.AuthGoogle.LogoutHandler) // shortcut, can be any of providers, all logouts do the same
|
||||
for _, provider := range s.AuthProviders {
|
||||
r.Mount("/"+provider.Name, provider.Routes())
|
||||
}
|
||||
r.Get("/logout", s.AuthProviders[0].LogoutHandler) // shortcut, can be any of providers, all logouts do the same
|
||||
})
|
||||
|
||||
// api routes
|
||||
@@ -96,7 +91,7 @@ func (s *Server) Run(port int) {
|
||||
rauth.Put("/vote/{id}", s.voteCtrl)
|
||||
|
||||
// admin routes, admin users only
|
||||
s.mod = admin{dataService: s.DataService, exporter: s.Exporter, respCache: s.respCache}
|
||||
s.mod = admin{dataService: s.DataService, exporter: s.Exporter, cache: s.Cache}
|
||||
rauth.Mount("/admin", s.mod.routes())
|
||||
})
|
||||
})
|
||||
@@ -157,7 +152,7 @@ func (s *Server) createCommentCtrl(w http.ResponseWriter, r *http.Request) {
|
||||
return
|
||||
}
|
||||
|
||||
s.respCache.flush() // reset all caches
|
||||
s.Cache.Flush() // reset all caches
|
||||
|
||||
render.Status(r, http.StatusCreated)
|
||||
render.JSON(w, r, JSON{"id": id, "loc": comment.Locator})
|
||||
@@ -206,7 +201,7 @@ func (s *Server) updateCommentCtrl(w http.ResponseWriter, r *http.Request) {
|
||||
return
|
||||
}
|
||||
|
||||
s.respCache.flush() // reset all caches
|
||||
s.Cache.Flush() // reset all caches
|
||||
render.JSON(w, r, res)
|
||||
}
|
||||
|
||||
@@ -216,7 +211,7 @@ func (s *Server) 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)
|
||||
|
||||
data, err := s.respCache.get(r.URL.String(), time.Hour, func() ([]byte, error) {
|
||||
data, err := s.Cache.Get(r.URL.String(), time.Hour, func() ([]byte, error) {
|
||||
comments, e := s.DataService.Find(locator, r.URL.Query().Get("sort"))
|
||||
if e != nil {
|
||||
return nil, e
|
||||
@@ -249,7 +244,7 @@ func (s *Server) lastCommentsCtrl(w http.ResponseWriter, r *http.Request) {
|
||||
max = 0
|
||||
}
|
||||
|
||||
data, err := s.respCache.get(r.URL.String(), time.Hour, func() ([]byte, error) {
|
||||
data, err := s.Cache.Get(r.URL.String(), time.Hour, func() ([]byte, error) {
|
||||
comments, e := s.DataService.Last(r.URL.Query().Get("site"), max)
|
||||
if e != nil {
|
||||
return nil, e
|
||||
@@ -291,7 +286,7 @@ func (s *Server) findUserCommentsCtrl(w http.ResponseWriter, r *http.Request) {
|
||||
|
||||
log.Printf("[DEBUG] get comments for userID %s, %s", userID, siteID)
|
||||
|
||||
data, err := s.respCache.get(r.URL.String(), time.Hour, func() ([]byte, error) {
|
||||
data, err := s.Cache.Get(r.URL.String(), time.Hour, func() ([]byte, error) {
|
||||
comments, e := s.DataService.User(siteID, userID)
|
||||
if e != nil {
|
||||
return nil, e
|
||||
@@ -331,7 +326,7 @@ func (s *Server) countCtrl(w http.ResponseWriter, r *http.Request) {
|
||||
func (s *Server) listCtrl(w http.ResponseWriter, r *http.Request) {
|
||||
|
||||
siteID := r.URL.Query().Get("site")
|
||||
data, err := s.respCache.get(r.URL.String(), 8*time.Hour, func() ([]byte, error) {
|
||||
data, err := s.Cache.Get(r.URL.String(), 8*time.Hour, func() ([]byte, error) {
|
||||
posts, e := s.DataService.List(siteID)
|
||||
if e != nil {
|
||||
return nil, e
|
||||
@@ -365,7 +360,7 @@ func (s *Server) voteCtrl(w http.ResponseWriter, r *http.Request) {
|
||||
common.SendErrorJSON(w, r, http.StatusBadRequest, err, "can't vote for comment")
|
||||
return
|
||||
}
|
||||
s.respCache.flush()
|
||||
s.Cache.Flush()
|
||||
render.JSON(w, r, JSON{"id": comment.ID, "score": comment.Score})
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user