integrate multi-site bolt store
This commit is contained in:
+12
-7
@@ -16,9 +16,10 @@ import (
|
||||
)
|
||||
|
||||
var opts struct {
|
||||
DBFile string `long:"db" env:"BOLTDB_FILE" default:"/tmp/remark.db" description:"bolt file name"`
|
||||
SiteURL string `long:"site-url" env:"REMARK_URL" default:"http://remark.umputun.com:8080" description:"url to remark"`
|
||||
Admins []string `long:"admin" env:"ADMIN" default:"umputun@gmail.com" description:"admin(s) names" env-delim:","`
|
||||
BoltPath string `long:"bolt" env:"BOLTDB_PATH" default:"/tmp" description:"parent dir for bolt files"`
|
||||
Sites []string `long:"site" env:"SITE" default:"demo" description:"site names" env-delim:","`
|
||||
RemarkURL string `long:"url" env:"REMARK_URL" default:"http://remark.umputun.com:8080" description:"url to remark"`
|
||||
Admins []string `long:"admin" env:"ADMIN" default:"umputun@gmail.com" description:"admin(s) names" env-delim:","`
|
||||
|
||||
DevMode bool `long:"dev" env:"DEV" description:"development mode, no auth enforced"`
|
||||
Dbg bool `long:"dbg" env:"DEBUG" description:"debug mode"`
|
||||
@@ -37,7 +38,7 @@ var opts struct {
|
||||
|
||||
ImportCommand struct {
|
||||
Provider string `long:"provider" default:"disqus" description:"provider type"`
|
||||
SiteID string `long:"site" default:"site" description:"site ID"`
|
||||
SiteID string `long:"site" default:"demo" description:"site ID"`
|
||||
InputFile string `long:"file" default:"disqus.xml" description:"input file"`
|
||||
} `command:"import" description:"import comments from external sources"`
|
||||
}
|
||||
@@ -54,7 +55,11 @@ func main() {
|
||||
setupLog(opts.Dbg)
|
||||
log.Print("[INFO] started remark")
|
||||
|
||||
dataStore, err := store.NewBoltDB(opts.DBFile)
|
||||
boltSites := []store.BoltSite{}
|
||||
for _, site := range opts.Sites {
|
||||
boltSites = append(boltSites, store.BoltSite{SiteID: site, FileName: fmt.Sprintf("%s/%s.db", opts.BoltPath, site)})
|
||||
}
|
||||
dataStore, err := store.NewBoltDB(boltSites...)
|
||||
if err != nil {
|
||||
log.Fatalf("[ERROR] can't initialize data store, %+v", err)
|
||||
}
|
||||
@@ -87,13 +92,13 @@ func main() {
|
||||
Cid: opts.ServerCommand.GoogleCID,
|
||||
Csecret: opts.ServerCommand.GoogleCSEC,
|
||||
SessionStore: sessionStore,
|
||||
SiteURL: opts.SiteURL,
|
||||
RemarkURL: opts.RemarkURL,
|
||||
}),
|
||||
AuthGithub: auth.NewGithub(auth.Params{
|
||||
Cid: opts.ServerCommand.GithubCID,
|
||||
Csecret: opts.ServerCommand.GithubCSEC,
|
||||
SessionStore: sessionStore,
|
||||
SiteURL: opts.SiteURL,
|
||||
RemarkURL: opts.RemarkURL,
|
||||
}),
|
||||
}
|
||||
|
||||
|
||||
@@ -33,7 +33,7 @@ func TestRemark_Export(t *testing.T) {
|
||||
func prep(t *testing.T) *store.BoltDB {
|
||||
os.Remove(testDb)
|
||||
|
||||
b, err := store.NewBoltDB(testDb)
|
||||
b, err := store.NewBoltDB(store.BoltSite{SiteID: "radio-t", FileName: testDb})
|
||||
assert.Nil(t, err)
|
||||
|
||||
comment := store.Comment{
|
||||
|
||||
+9
-9
@@ -30,14 +30,14 @@ func (a *admin) routes() chi.Router {
|
||||
return router
|
||||
}
|
||||
|
||||
// DELETE /comment/{id}?url=post-url
|
||||
// DELETE /comment/{id}?site=siteID&url=post-url
|
||||
func (a *admin) deleteCommentCtrl(w http.ResponseWriter, r *http.Request) {
|
||||
|
||||
id := chi.URLParam(r, "id")
|
||||
locator := store.Locator{SiteID: r.URL.Query().Get("site"), URL: r.URL.Query().Get("url")}
|
||||
log.Printf("[INFO] delete comment %s", id)
|
||||
|
||||
url := r.URL.Query().Get("url")
|
||||
err := a.dataService.Delete(store.Locator{URL: url}, id)
|
||||
err := a.dataService.Delete(locator, id)
|
||||
if err != nil {
|
||||
log.Printf("[WARN] can't delete comment, %s", err)
|
||||
httpError(w, r, http.StatusInternalServerError, err, "can't delete comment")
|
||||
@@ -45,7 +45,7 @@ func (a *admin) deleteCommentCtrl(w http.ResponseWriter, r *http.Request) {
|
||||
}
|
||||
a.respCache.Flush()
|
||||
render.Status(r, http.StatusOK)
|
||||
render.JSON(w, r, JSON{"id": id, "url": url})
|
||||
render.JSON(w, r, JSON{"id": id, "loc": locator})
|
||||
}
|
||||
|
||||
// PUT /user/{userid}?site=side-id&block=1
|
||||
@@ -62,18 +62,18 @@ func (a *admin) setBlockCtrl(w http.ResponseWriter, r *http.Request) {
|
||||
render.JSON(w, r, JSON{"user_id": userID, "site_id": siteID, "block": blockStatus})
|
||||
}
|
||||
|
||||
// PUT /pin/{id}?url=post-url&pin=1
|
||||
// PUT /pin/{id}?site=siteID&url=post-url&pin=1
|
||||
func (a *admin) setPinCtrl(w http.ResponseWriter, r *http.Request) {
|
||||
commentID := chi.URLParam(r, "id")
|
||||
url := r.URL.Query().Get("url")
|
||||
locator := store.Locator{SiteID: r.URL.Query().Get("site"), URL: r.URL.Query().Get("url")}
|
||||
pinStatus := r.URL.Query().Get("pin") == "1"
|
||||
|
||||
if err := a.dataService.SetPin(store.Locator{URL: url}, commentID, pinStatus); err != nil {
|
||||
if err := a.dataService.SetPin(locator, commentID, pinStatus); err != nil {
|
||||
httpError(w, r, http.StatusBadRequest, err, "can't set pin status")
|
||||
return
|
||||
}
|
||||
a.respCache.Flush()
|
||||
render.JSON(w, r, JSON{"id": commentID, "url": url, "pin": pinStatus})
|
||||
render.JSON(w, r, JSON{"id": commentID, "loc": locator, "pin": pinStatus})
|
||||
}
|
||||
|
||||
// GET /export?site=site-id
|
||||
@@ -84,5 +84,5 @@ func (a *admin) exportCtrl(w http.ResponseWriter, r *http.Request) {
|
||||
}
|
||||
}
|
||||
func (a *admin) checkBlocked(locator store.Locator, user store.User) bool {
|
||||
return a.dataService.IsBlocked(store.Locator{}, user.ID)
|
||||
return a.dataService.IsBlocked(locator, user.ID)
|
||||
}
|
||||
|
||||
@@ -37,7 +37,7 @@ type Params struct {
|
||||
Cid string
|
||||
Csecret string
|
||||
SessionStore *sessions.FilesystemStore
|
||||
SiteURL string
|
||||
RemarkURL string
|
||||
}
|
||||
|
||||
// newProvider makes auth for given provider
|
||||
|
||||
@@ -14,7 +14,7 @@ func NewGoogle(p Params) *Provider {
|
||||
return initProvider(p, Provider{
|
||||
Name: "google",
|
||||
Endpoint: google.Endpoint,
|
||||
RedirectURL: p.SiteURL + "/auth/google",
|
||||
RedirectURL: p.RemarkURL + "/auth/google",
|
||||
Scopes: []string{"https://www.googleapis.com/auth/userinfo.email"},
|
||||
InfoURL: "https://www.googleapis.com/oauth2/v3/userinfo",
|
||||
FilesystemStore: p.SessionStore,
|
||||
@@ -39,7 +39,7 @@ func NewGithub(p Params) *Provider {
|
||||
return initProvider(p, Provider{
|
||||
Name: "github",
|
||||
Endpoint: github.Endpoint,
|
||||
RedirectURL: p.SiteURL + "/auth/github",
|
||||
RedirectURL: p.RemarkURL + "/auth/github",
|
||||
Scopes: []string{"user:email"},
|
||||
InfoURL: "https://api.github.com/user",
|
||||
FilesystemStore: p.SessionStore,
|
||||
|
||||
+26
-27
@@ -150,19 +150,19 @@ func (s *Server) createCommentCtrl(w http.ResponseWriter, r *http.Request) {
|
||||
s.respCache.Flush()
|
||||
|
||||
render.Status(r, http.StatusAccepted)
|
||||
render.JSON(w, r, JSON{"id": id, "url": comment.Locator.URL})
|
||||
render.JSON(w, r, JSON{"id": id, "loc": comment.Locator})
|
||||
}
|
||||
|
||||
// DELETE /comment/{id}?url=post-url
|
||||
// DELETE /comment/{id}?site=siteID&url=post-url
|
||||
func (s *Server) deleteCommentCtrl(w http.ResponseWriter, r *http.Request) {
|
||||
|
||||
id := chi.URLParam(r, "id")
|
||||
log.Printf("[DEBUG] delete comment %s", id)
|
||||
|
||||
url := r.URL.Query().Get("url")
|
||||
err := s.DataService.Delete(store.Locator{URL: url}, id)
|
||||
locator := store.Locator{SiteID: r.URL.Query().Get("site"), URL: r.URL.Query().Get("url")}
|
||||
err := s.DataService.Delete(locator, id)
|
||||
if err != nil {
|
||||
log.Printf("[WARN] can't delete comment, %s", err)
|
||||
log.Printf("[WARN] can't delete comment %s %+v, %s", id, locator, err)
|
||||
httpError(w, r, http.StatusInternalServerError, err, "can't delete comment")
|
||||
return
|
||||
}
|
||||
@@ -170,13 +170,13 @@ func (s *Server) deleteCommentCtrl(w http.ResponseWriter, r *http.Request) {
|
||||
s.respCache.Flush()
|
||||
|
||||
render.Status(r, http.StatusOK)
|
||||
render.JSON(w, r, JSON{"id": id, "url": url})
|
||||
render.JSON(w, r, JSON{"id": id, "loc": locator})
|
||||
}
|
||||
|
||||
// GET /find?url=post-url&format=tree&sort=-time
|
||||
// GET /find?site=siteID&url=post-url&format=tree&sort=-time
|
||||
func (s *Server) findCommentsCtrl(w http.ResponseWriter, r *http.Request) {
|
||||
url := r.URL.Query().Get("url")
|
||||
log.Printf("[DEBUG] get comments for %s", url)
|
||||
locator := store.Locator{SiteID: r.URL.Query().Get("site"), URL: r.URL.Query().Get("url")}
|
||||
log.Printf("[DEBUG] get comments for %+v", locator)
|
||||
|
||||
cacheKey := r.URL.String()
|
||||
if comments, ok := s.respCache.Get(cacheKey); ok {
|
||||
@@ -185,9 +185,9 @@ func (s *Server) findCommentsCtrl(w http.ResponseWriter, r *http.Request) {
|
||||
return
|
||||
}
|
||||
|
||||
comments, err := s.DataService.Find(store.Request{Locator: store.Locator{URL: url}, Sort: r.URL.Query().Get("sort")})
|
||||
comments, err := s.DataService.Find(store.Request{Locator: locator, Sort: r.URL.Query().Get("sort")})
|
||||
if err != nil {
|
||||
log.Printf("[WARN] can't get comments for %s, %s", url, err)
|
||||
log.Printf("[WARN] can't get comments for %+v, %s", locator, err)
|
||||
httpError(w, r, http.StatusInternalServerError, err, "can't load comments comment")
|
||||
return
|
||||
}
|
||||
@@ -201,7 +201,7 @@ func (s *Server) findCommentsCtrl(w http.ResponseWriter, r *http.Request) {
|
||||
renderJSONWithHTML(w, r, comments)
|
||||
}
|
||||
|
||||
// GET /last/{max}?url=abc
|
||||
// GET /last/{max}?site=siteID
|
||||
func (s *Server) lastCommentsCtrl(w http.ResponseWriter, r *http.Request) {
|
||||
|
||||
max, err := strconv.Atoi(chi.URLParam(r, "max"))
|
||||
@@ -216,7 +216,7 @@ func (s *Server) lastCommentsCtrl(w http.ResponseWriter, r *http.Request) {
|
||||
return
|
||||
}
|
||||
|
||||
comments, err := s.DataService.Last(store.Locator{}, max)
|
||||
comments, err := s.DataService.Last(store.Locator{SiteID: r.URL.Query().Get("site")}, max)
|
||||
if err != nil {
|
||||
log.Printf("[WARN] can't get last comments, %s", err)
|
||||
httpError(w, r, http.StatusInternalServerError, err, "can't get last comments")
|
||||
@@ -228,15 +228,15 @@ func (s *Server) lastCommentsCtrl(w http.ResponseWriter, r *http.Request) {
|
||||
renderJSONWithHTML(w, r, comments)
|
||||
}
|
||||
|
||||
// GET /id/{id}?url=post-url
|
||||
// GET /id/{id}?site=siteID&url=post-url
|
||||
func (s *Server) commentByIDCtrl(w http.ResponseWriter, r *http.Request) {
|
||||
|
||||
id := chi.URLParam(r, "id")
|
||||
url := r.URL.Query().Get("url")
|
||||
locator := store.Locator{SiteID: r.URL.Query().Get("site"), URL: r.URL.Query().Get("url")}
|
||||
|
||||
log.Printf("[DEBUG] get comments by id %s, %s", id, url)
|
||||
log.Printf("[DEBUG] get comments by id %s, %+v", id, locator)
|
||||
|
||||
comment, err := s.DataService.GetByID(store.Locator{URL: url}, id)
|
||||
comment, err := s.DataService.GetByID(locator, id)
|
||||
if err != nil {
|
||||
log.Printf("[WARN] can't get comment, %s", err)
|
||||
httpError(w, r, http.StatusInternalServerError, err, "can't get comment by id")
|
||||
@@ -246,7 +246,7 @@ func (s *Server) commentByIDCtrl(w http.ResponseWriter, r *http.Request) {
|
||||
renderJSONWithHTML(w, r, comment)
|
||||
}
|
||||
|
||||
// GET /comments?user=id
|
||||
// GET /comments?site=siteID&user=id
|
||||
func (s *Server) findUserCommentsCtrl(w http.ResponseWriter, r *http.Request) {
|
||||
|
||||
userID := r.URL.Query().Get("user")
|
||||
@@ -260,7 +260,7 @@ func (s *Server) findUserCommentsCtrl(w http.ResponseWriter, r *http.Request) {
|
||||
return
|
||||
}
|
||||
|
||||
comments, err := s.DataService.GetByUser(store.Locator{}, userID)
|
||||
comments, err := s.DataService.GetByUser(store.Locator{SiteID: r.URL.Query().Get("site")}, userID)
|
||||
if err != nil {
|
||||
log.Printf("[WARN] can't get comment, %s", err)
|
||||
httpError(w, r, http.StatusBadRequest, err, "can't get comment by user id")
|
||||
@@ -282,18 +282,18 @@ func (s *Server) userInfoCtrl(w http.ResponseWriter, r *http.Request) {
|
||||
render.JSON(w, r, user)
|
||||
}
|
||||
|
||||
// GET /count?url=post-url
|
||||
// GET /count?site=siteID&url=post-url
|
||||
func (s *Server) countCtrl(w http.ResponseWriter, r *http.Request) {
|
||||
url := r.URL.Query().Get("url")
|
||||
count, err := s.DataService.Count(store.Locator{URL: url})
|
||||
locator := store.Locator{SiteID: r.URL.Query().Get("site"), URL: r.URL.Query().Get("url")}
|
||||
count, err := s.DataService.Count(locator)
|
||||
if err != nil {
|
||||
httpError(w, r, http.StatusBadRequest, err, "can't get count")
|
||||
return
|
||||
}
|
||||
render.JSON(w, r, JSON{"count": count, "url": url})
|
||||
render.JSON(w, r, JSON{"count": count, "loc": locator})
|
||||
}
|
||||
|
||||
// PUT /vote/{id}?url=post-url&vote=1
|
||||
// PUT /vote/{id}?site=siteID&url=post-url&vote=1
|
||||
func (s *Server) voteCtrl(w http.ResponseWriter, r *http.Request) {
|
||||
|
||||
user, err := auth.GetUserInfo(r)
|
||||
@@ -301,14 +301,13 @@ func (s *Server) voteCtrl(w http.ResponseWriter, r *http.Request) {
|
||||
httpError(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")}
|
||||
id := chi.URLParam(r, "id")
|
||||
log.Printf("[DEBUG] vote for comment %s", id)
|
||||
|
||||
url := r.URL.Query().Get("url")
|
||||
vote := r.URL.Query().Get("vote") == "1"
|
||||
|
||||
comment, err := s.DataService.Vote(store.Locator{URL: url}, id, user.ID, vote)
|
||||
comment, err := s.DataService.Vote(locator, id, user.ID, vote)
|
||||
if err != nil {
|
||||
log.Printf("[WARN] vote rejected for %s - %s, %s", user.ID, id, err)
|
||||
httpError(w, r, http.StatusBadRequest, err, "can't vote for comment")
|
||||
|
||||
+98
-21
@@ -21,7 +21,7 @@ import (
|
||||
// is a bucket with ts:reference
|
||||
// - blocking info sits in "block" bucket. Key is userID, value - ts
|
||||
type BoltDB struct {
|
||||
*bolt.DB
|
||||
dbs map[string]*bolt.DB
|
||||
}
|
||||
|
||||
const (
|
||||
@@ -32,16 +32,24 @@ const (
|
||||
userLimit = 100
|
||||
)
|
||||
|
||||
// BoltSite defines single site param
|
||||
type BoltSite struct {
|
||||
FileName string
|
||||
SiteID string
|
||||
}
|
||||
|
||||
// NewBoltDB makes persistent boltdb-based store
|
||||
func NewBoltDB(dbFile string) (*BoltDB, error) {
|
||||
log.Printf("[INFO] bolt store, %s", dbFile)
|
||||
result := BoltDB{}
|
||||
db, err := bolt.Open(dbFile, 0600, &bolt.Options{Timeout: 5 * time.Second})
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(err, "failed to make boltdb for %s", dbFile)
|
||||
func NewBoltDB(sites ...BoltSite) (*BoltDB, error) {
|
||||
log.Printf("[INFO] bolt store for sites %+v", sites)
|
||||
result := BoltDB{dbs: make(map[string]*bolt.DB)}
|
||||
for _, site := range sites {
|
||||
db, err := bolt.Open(site.FileName, 0600, &bolt.Options{Timeout: 5 * time.Second})
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(err, "failed to make boltdb for %s", site.FileName)
|
||||
}
|
||||
result.dbs[site.SiteID] = db
|
||||
}
|
||||
result.DB = db
|
||||
return &result, err
|
||||
return &result, nil
|
||||
}
|
||||
|
||||
// Create saves new comment to store
|
||||
@@ -57,7 +65,11 @@ func (b *BoltDB) Create(comment Comment) (string, error) {
|
||||
comment.Votes = make(map[string]bool)
|
||||
comment = sanitizeComment(comment) // clear potentially dangerous js from all parts of comment
|
||||
|
||||
err := b.Update(func(tx *bolt.Tx) error {
|
||||
bdb, err := b.db(comment.Locator.SiteID)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
err = bdb.Update(func(tx *bolt.Tx) error {
|
||||
bucket, e := tx.CreateBucketIfNotExists([]byte(comment.Locator.URL)) // bucket per post url
|
||||
if e != nil {
|
||||
return errors.Wrapf(e, "can't make or open bucket", comment.Locator.URL)
|
||||
@@ -112,7 +124,12 @@ func (b *BoltDB) Create(comment Comment) (string, error) {
|
||||
// Delete removes comment locator from the store
|
||||
func (b *BoltDB) Delete(locator Locator, commentID string) error {
|
||||
|
||||
return b.Update(func(tx *bolt.Tx) error {
|
||||
bdb, err := b.db(locator.SiteID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return bdb.Update(func(tx *bolt.Tx) error {
|
||||
bucket := tx.Bucket([]byte(locator.URL))
|
||||
if bucket == nil {
|
||||
return errors.Errorf("no bucket %s in store", locator.URL)
|
||||
@@ -139,7 +156,12 @@ func (b *BoltDB) Delete(locator Locator, commentID string) error {
|
||||
func (b *BoltDB) Find(request Request) ([]Comment, error) {
|
||||
res := []Comment{}
|
||||
|
||||
err := b.View(func(tx *bolt.Tx) error {
|
||||
bdb, err := b.db(request.Locator.SiteID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
err = bdb.View(func(tx *bolt.Tx) error {
|
||||
bucket := tx.Bucket([]byte(request.Locator.URL))
|
||||
if bucket == nil {
|
||||
return errors.Errorf("no bucket %s in store", request.Locator.URL)
|
||||
@@ -181,7 +203,12 @@ func (b *BoltDB) Find(request Request) ([]Comment, error) {
|
||||
// GetByID returns comment by id across posts
|
||||
func (b *BoltDB) GetByID(locator Locator, commentID string) (comment Comment, err error) {
|
||||
|
||||
err = b.View(func(tx *bolt.Tx) error {
|
||||
bdb, err := b.db(locator.SiteID)
|
||||
if err != nil {
|
||||
return comment, err
|
||||
}
|
||||
|
||||
err = bdb.View(func(tx *bolt.Tx) error {
|
||||
|
||||
lastBucket := tx.Bucket([]byte(lastBucketName))
|
||||
if lastBucket == nil {
|
||||
@@ -224,7 +251,12 @@ func (b *BoltDB) Last(locator Locator, max int) (result []Comment, err error) {
|
||||
max = lastLimit
|
||||
}
|
||||
|
||||
err = b.View(func(tx *bolt.Tx) error {
|
||||
bdb, err := b.db(locator.SiteID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
err = bdb.View(func(tx *bolt.Tx) error {
|
||||
lastBucket := tx.Bucket([]byte(lastBucketName))
|
||||
if lastBucket == nil {
|
||||
return errors.Errorf("no bucket %s in store", lastBucketName)
|
||||
@@ -264,7 +296,13 @@ func (b *BoltDB) Last(locator Locator, max int) (result []Comment, err error) {
|
||||
|
||||
// Count returns number of comments for locator
|
||||
func (b *BoltDB) Count(locator Locator) (count int, err error) {
|
||||
err = b.View(func(tx *bolt.Tx) error {
|
||||
|
||||
bdb, err := b.db(locator.SiteID)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
err = bdb.View(func(tx *bolt.Tx) error {
|
||||
bucket := tx.Bucket([]byte(locator.URL))
|
||||
if bucket == nil {
|
||||
return errors.Errorf("no bucket %s in store", locator.URL)
|
||||
@@ -278,7 +316,13 @@ func (b *BoltDB) Count(locator Locator) (count int, err error) {
|
||||
|
||||
// SetBlock blocks/unblocks user for given site
|
||||
func (b *BoltDB) SetBlock(locator Locator, userID string, status bool) error {
|
||||
return b.Update(func(tx *bolt.Tx) error {
|
||||
|
||||
bdb, err := b.db(locator.SiteID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return bdb.Update(func(tx *bolt.Tx) error {
|
||||
|
||||
bucket, e := tx.CreateBucketIfNotExists([]byte(blocksBucketName))
|
||||
if e != nil {
|
||||
@@ -301,7 +345,13 @@ func (b *BoltDB) SetBlock(locator Locator, userID string, status bool) error {
|
||||
|
||||
// IsBlocked checks if user blocked
|
||||
func (b *BoltDB) IsBlocked(locator Locator, userID string) (result bool) {
|
||||
_ = b.View(func(tx *bolt.Tx) error {
|
||||
|
||||
bdb, err := b.db(locator.SiteID)
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
|
||||
_ = bdb.View(func(tx *bolt.Tx) error {
|
||||
result = false
|
||||
bucket := tx.Bucket([]byte(blocksBucketName))
|
||||
if bucket != nil && bucket.Get([]byte(userID)) != nil {
|
||||
@@ -314,7 +364,13 @@ func (b *BoltDB) IsBlocked(locator Locator, userID string) (result bool) {
|
||||
|
||||
// List returns list of buckets, which is list of all commented posts
|
||||
func (b BoltDB) List(locator Locator) (result []string, err error) {
|
||||
err = b.View(func(tx *bolt.Tx) error {
|
||||
|
||||
bdb, err := b.db(locator.SiteID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
err = bdb.View(func(tx *bolt.Tx) error {
|
||||
return tx.ForEach(func(name []byte, _ *bolt.Bucket) error {
|
||||
if string(name) != lastBucketName && string(name) != userBucketName {
|
||||
result = append(result, string(name))
|
||||
@@ -332,8 +388,12 @@ func (b *BoltDB) GetByUser(locator Locator, userID string) (comments []Comment,
|
||||
comments = []Comment{}
|
||||
commentRefs := []string{}
|
||||
|
||||
bdb, err := b.db(locator.SiteID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
// get list of references to comments
|
||||
err = b.View(func(tx *bolt.Tx) error {
|
||||
err = bdb.View(func(tx *bolt.Tx) error {
|
||||
userBucket := tx.Bucket([]byte(userBucketName))
|
||||
if userBucket == nil {
|
||||
return errors.Errorf("no bucket %s in store", userBucketName)
|
||||
@@ -375,7 +435,12 @@ func (b *BoltDB) GetByUser(locator Locator, userID string) (comments []Comment,
|
||||
// Get for locator.URL and commentID string
|
||||
func (b *BoltDB) Get(locator Locator, commentID string) (comment Comment, err error) {
|
||||
|
||||
err = b.View(func(tx *bolt.Tx) error {
|
||||
bdb, err := b.db(locator.SiteID)
|
||||
if err != nil {
|
||||
return comment, err
|
||||
}
|
||||
|
||||
err = bdb.View(func(tx *bolt.Tx) error {
|
||||
bucket := tx.Bucket([]byte(locator.URL))
|
||||
if bucket == nil {
|
||||
return errors.Errorf("no bucket %s in store", locator.URL)
|
||||
@@ -406,7 +471,12 @@ func (b *BoltDB) Put(locator Locator, comment Comment) error {
|
||||
comment.User = curComment.User
|
||||
}
|
||||
|
||||
return b.Update(func(tx *bolt.Tx) error {
|
||||
bdb, err := b.db(locator.SiteID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return bdb.Update(func(tx *bolt.Tx) error {
|
||||
bucket := tx.Bucket([]byte(locator.URL))
|
||||
if bucket == nil {
|
||||
return errors.Errorf("no bucket %s in store", locator.URL)
|
||||
@@ -424,6 +494,13 @@ func (b *BoltDB) Put(locator Locator, comment Comment) error {
|
||||
})
|
||||
}
|
||||
|
||||
func (b *BoltDB) db(siteID string) (*bolt.DB, error) {
|
||||
if res, ok := b.dbs[siteID]; ok {
|
||||
return res, nil
|
||||
}
|
||||
return nil, errors.Errorf("site %s not found", siteID)
|
||||
}
|
||||
|
||||
// ref represents key:value pair for extra, index-only buckets
|
||||
type ref struct {
|
||||
key string
|
||||
|
||||
+22
-18
@@ -15,7 +15,7 @@ func TestBoltDB_CreateAndFind(t *testing.T) {
|
||||
defer os.Remove(testDb)
|
||||
b = prep(t)
|
||||
|
||||
res, err := b.Find(Request{Locator: Locator{URL: "https://radio-t.com"}})
|
||||
res, err := b.Find(Request{Locator: Locator{URL: "https://radio-t.com", SiteID: "radio-t"}})
|
||||
assert.Nil(t, err)
|
||||
assert.Equal(t, 2, len(res))
|
||||
assert.Equal(t, `some text, <a href="http://radio-t.com" rel="nofollow">link</a>`, res[0].Text)
|
||||
@@ -27,7 +27,7 @@ func TestBoltDB_Delete(t *testing.T) {
|
||||
defer os.Remove(testDb)
|
||||
b := prep(t)
|
||||
|
||||
loc := Locator{URL: "https://radio-t.com"}
|
||||
loc := Locator{URL: "https://radio-t.com", SiteID: "radio-t"}
|
||||
res, err := b.Find(Request{Locator: loc})
|
||||
assert.Nil(t, err)
|
||||
assert.Equal(t, 2, len(res))
|
||||
@@ -49,15 +49,15 @@ func TestBoltDB_GetByID(t *testing.T) {
|
||||
defer os.Remove(testDb)
|
||||
b := prep(t)
|
||||
|
||||
res, err := b.Find(Request{Locator: Locator{URL: "https://radio-t.com"}})
|
||||
res, err := b.Find(Request{Locator: Locator{URL: "https://radio-t.com", SiteID: "radio-t"}})
|
||||
assert.Nil(t, err)
|
||||
assert.Equal(t, 2, len(res))
|
||||
|
||||
comment, err := b.GetByID(Locator{URL: "https://radio-t.com"}, res[1].ID)
|
||||
comment, err := b.GetByID(Locator{URL: "https://radio-t.com", SiteID: "radio-t"}, res[1].ID)
|
||||
assert.Nil(t, err)
|
||||
assert.Equal(t, "some text2", comment.Text)
|
||||
|
||||
comment, err = b.GetByID(Locator{URL: "https://radio-t.com"}, "1234567")
|
||||
comment, err = b.GetByID(Locator{URL: "https://radio-t.com", SiteID: "radio-t"}, "1234567")
|
||||
assert.NotNil(t, err)
|
||||
}
|
||||
|
||||
@@ -65,12 +65,12 @@ func TestBoltDB_Last(t *testing.T) {
|
||||
defer os.Remove(testDb)
|
||||
b := prep(t)
|
||||
|
||||
res, err := b.Last(Locator{URL: "https://radio-t.com"}, 0)
|
||||
res, err := b.Last(Locator{URL: "https://radio-t.com", SiteID: "radio-t"}, 0)
|
||||
assert.Nil(t, err)
|
||||
assert.Equal(t, 2, len(res))
|
||||
assert.Equal(t, "some text2", res[0].Text)
|
||||
|
||||
res, err = b.Last(Locator{URL: "https://radio-t.com"}, 1)
|
||||
res, err = b.Last(Locator{URL: "https://radio-t.com", SiteID: "radio-t"}, 1)
|
||||
assert.Nil(t, err)
|
||||
assert.Equal(t, 1, len(res))
|
||||
assert.Equal(t, "some text2", res[0].Text)
|
||||
@@ -80,7 +80,7 @@ func TestBoltDB_Count(t *testing.T) {
|
||||
defer os.Remove(testDb)
|
||||
b := prep(t)
|
||||
|
||||
c, err := b.Count(Locator{URL: "https://radio-t.com"})
|
||||
c, err := b.Count(Locator{URL: "https://radio-t.com", SiteID: "radio-t"})
|
||||
assert.Nil(t, err)
|
||||
assert.Equal(t, 2, c)
|
||||
}
|
||||
@@ -89,15 +89,15 @@ func TestBoltDB_BlockUser(t *testing.T) {
|
||||
defer os.Remove(testDb)
|
||||
b := prep(t)
|
||||
|
||||
assert.False(t, b.IsBlocked(Locator{SiteID: "site1"}, "user1"), "nothing blocked")
|
||||
assert.False(t, b.IsBlocked(Locator{SiteID: "radio-t"}, "user1"), "nothing blocked")
|
||||
|
||||
assert.NoError(t, b.SetBlock(Locator{SiteID: "site1"}, "user1", true))
|
||||
assert.True(t, b.IsBlocked(Locator{SiteID: "site1"}, "user1"), "user1 blocked")
|
||||
assert.NoError(t, b.SetBlock(Locator{SiteID: "radio-t"}, "user1", true))
|
||||
assert.True(t, b.IsBlocked(Locator{SiteID: "radio-t"}, "user1"), "user1 blocked")
|
||||
|
||||
assert.False(t, b.IsBlocked(Locator{SiteID: "site1"}, "user2"), "user2 still unblocked")
|
||||
assert.False(t, b.IsBlocked(Locator{SiteID: "radio-t"}, "user2"), "user2 still unblocked")
|
||||
|
||||
assert.NoError(t, b.SetBlock(Locator{SiteID: "site1"}, "user1", false))
|
||||
assert.False(t, b.IsBlocked(Locator{SiteID: "site1"}, "user1"), "user1 unblocked")
|
||||
assert.NoError(t, b.SetBlock(Locator{SiteID: "radio-t"}, "user1", false))
|
||||
assert.False(t, b.IsBlocked(Locator{SiteID: "radio-t"}, "user1"), "user1 unblocked")
|
||||
|
||||
}
|
||||
|
||||
@@ -106,12 +106,16 @@ func TestBoltDB_List(t *testing.T) {
|
||||
b := prep(t) // two comments for https://radio-t.com
|
||||
|
||||
// add one more for https://radio-t.com/2
|
||||
comment := Comment{Text: `some text, <a href="http://radio-t.com">link</a>`, Timestamp: time.Date(2017, 12, 20, 15, 18, 22, 0, time.Local),
|
||||
Locator: Locator{URL: "https://radio-t.com/2", SiteID: "radio-t"}, User: User{ID: "user1", Name: "user name"}}
|
||||
comment := Comment{
|
||||
Text: `some text, <a href="http://radio-t.com">link</a>`,
|
||||
Timestamp: time.Date(2017, 12, 20, 15, 18, 22, 0, time.Local),
|
||||
Locator: Locator{URL: "https://radio-t.com/2", SiteID: "radio-t"},
|
||||
User: User{ID: "user1", Name: "user name"},
|
||||
}
|
||||
_, err := b.Create(comment)
|
||||
assert.Nil(t, err)
|
||||
|
||||
res, err := b.List(Locator{SiteID: "site1"})
|
||||
res, err := b.List(Locator{SiteID: "radio-t"})
|
||||
assert.Nil(t, err)
|
||||
assert.Equal(t, []string{"https://radio-t.com", "https://radio-t.com/2"}, res)
|
||||
}
|
||||
@@ -130,7 +134,7 @@ func TestBoltDB_GetForUser(t *testing.T) {
|
||||
func prep(t *testing.T) *BoltDB {
|
||||
os.Remove(testDb)
|
||||
|
||||
b, err := NewBoltDB(testDb)
|
||||
b, err := NewBoltDB(BoltSite{FileName: "/tmp/test-remark.db", SiteID: "radio-t"})
|
||||
assert.Nil(t, err)
|
||||
|
||||
comment := Comment{
|
||||
|
||||
@@ -11,22 +11,22 @@ func TestService_Vote(t *testing.T) {
|
||||
defer os.Remove(testDb)
|
||||
b := Service{Interface: prep(t)}
|
||||
|
||||
res, err := b.Last(Locator{URL: "https://radio-t.com"}, 0)
|
||||
res, err := b.Last(Locator{URL: "https://radio-t.com", SiteID: "radio-t"}, 0)
|
||||
t.Logf("%+v", res[0])
|
||||
assert.Nil(t, err)
|
||||
assert.Equal(t, 2, len(res))
|
||||
assert.Equal(t, 0, res[0].Score)
|
||||
assert.Equal(t, map[string]bool{}, res[0].Votes)
|
||||
|
||||
c, err := b.Vote(Locator{URL: "https://radio-t.com"}, res[0].ID, "user1", true)
|
||||
c, err := b.Vote(Locator{URL: "https://radio-t.com", SiteID: "radio-t"}, res[0].ID, "user1", true)
|
||||
assert.Nil(t, err)
|
||||
assert.Equal(t, 1, c.Score)
|
||||
assert.Equal(t, map[string]bool{"user1": true}, c.Votes)
|
||||
|
||||
_, err = b.Vote(Locator{URL: "https://radio-t.com"}, res[0].ID, "user1", true)
|
||||
_, err = b.Vote(Locator{URL: "https://radio-t.com", SiteID: "radio-t"}, res[0].ID, "user1", true)
|
||||
assert.NotNil(t, err, "double-voting rejected")
|
||||
|
||||
res, err = b.Last(Locator{URL: "https://radio-t.com"}, 0)
|
||||
res, err = b.Last(Locator{URL: "https://radio-t.com", SiteID: "radio-t"}, 0)
|
||||
assert.Nil(t, err)
|
||||
assert.Equal(t, 2, len(res))
|
||||
assert.Equal(t, 1, res[0].Score)
|
||||
@@ -36,22 +36,22 @@ func TestBoltDB_Pin(t *testing.T) {
|
||||
defer os.Remove(testDb)
|
||||
b := Service{Interface: prep(t)}
|
||||
|
||||
res, err := b.Last(Locator{URL: "https://radio-t.com"}, 0)
|
||||
res, err := b.Last(Locator{URL: "https://radio-t.com", SiteID: "radio-t"}, 0)
|
||||
t.Logf("%+v", res[0])
|
||||
assert.Nil(t, err)
|
||||
assert.Equal(t, 2, len(res))
|
||||
assert.Equal(t, false, res[0].Pin)
|
||||
|
||||
err = b.SetPin(Locator{URL: "https://radio-t.com"}, res[0].ID, true)
|
||||
err = b.SetPin(Locator{URL: "https://radio-t.com", SiteID: "radio-t"}, res[0].ID, true)
|
||||
assert.Nil(t, err)
|
||||
|
||||
c, err := b.GetByID(Locator{URL: "https://radio-t.com"}, res[0].ID)
|
||||
c, err := b.GetByID(Locator{URL: "https://radio-t.com", SiteID: "radio-t"}, res[0].ID)
|
||||
assert.Nil(t, err)
|
||||
assert.Equal(t, true, c.Pin)
|
||||
|
||||
err = b.SetPin(Locator{URL: "https://radio-t.com"}, res[0].ID, false)
|
||||
err = b.SetPin(Locator{URL: "https://radio-t.com", SiteID: "radio-t"}, res[0].ID, false)
|
||||
assert.Nil(t, err)
|
||||
c, err = b.GetByID(Locator{URL: "https://radio-t.com"}, res[0].ID)
|
||||
c, err = b.GetByID(Locator{URL: "https://radio-t.com", SiteID: "radio-t"}, res[0].ID)
|
||||
assert.Nil(t, err)
|
||||
assert.Equal(t, false, c.Pin)
|
||||
}
|
||||
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
|
||||
### find request with tree
|
||||
GET https://demo.remark42.com/api/v1/find?url=https://radio-t.com/p/2017/12/16/podcast-576/&sort=time&format=tree"
|
||||
GET https://demo.remark42.com/api/v1/find?site=demo&url=https://radio-t.com/p/2017/12/16/podcast-576/&sort=time&format=tree"
|
||||
|
||||
### find request with plain
|
||||
GET https://demo.remark42.com/api/v1/find?url=https://radio-t.com/p/2017/12/16/podcast-576/&sort=time&format=plain"
|
||||
|
||||
Reference in New Issue
Block a user