From 0363fc4cd99d2a4ed74640d8fe2fa80465b6ca83 Mon Sep 17 00:00:00 2001 From: Umputun Date: Tue, 29 May 2018 23:07:50 -0500 Subject: [PATCH] feature/verified (#61) * add store methods for verification * add verify rest and auth * lint: fix double conversion of userID key * test for middleware, fix log without auth info * make all scripts with -e --- app/main.go | 20 ++++----- app/rest/api/admin.go | 15 +++++++ app/rest/api/admin_test.go | 38 +++++++++++++++++ app/rest/api/middleware_test.go | 63 +++++++++++++++++++++++++++++ app/rest/api/rest.go | 6 ++- app/rest/auth/jwt.go | 5 ++- app/rest/auth/provider.go | 26 +++++++----- app/rest/auth/provider_test.go | 9 +++-- app/rest/user.go | 1 - app/store/engine/bolt_accessor.go | 3 +- app/store/engine/bolt_admin.go | 41 ++++++++++++++++++- app/store/engine/bolt_admin_test.go | 19 +++++++++ app/store/engine/engine.go | 2 + app/store/service/service.go | 10 +++++ app/store/user.go | 13 +++--- remark.rest | 36 ++++++++--------- scripts/create-backup.sh | 2 +- scripts/import-disqus.sh | 1 + scripts/migrate-data.sh | 6 +-- scripts/restore-backup.sh | 3 +- 20 files changed, 260 insertions(+), 59 deletions(-) create mode 100644 app/rest/api/middleware_test.go diff --git a/app/main.go b/app/main.go index bc7b0b8f..a02a50e1 100644 --- a/app/main.go +++ b/app/main.go @@ -147,7 +147,7 @@ func New(opts Opts) (*Application, error) { Authenticator: auth.Authenticator{ JWTService: jwtService, Admins: opts.Admins, - Providers: makeAuthProviders(jwtService, avatarProxy, opts), + Providers: makeAuthProviders(jwtService, avatarProxy, dataService, opts), DevPasswd: opts.DevPasswd, }, Cache: cache, @@ -237,20 +237,22 @@ func makeDirs(dirs ...string) error { return nil } -func makeAuthProviders(jwtService *auth.JWT, avatarProxy *proxy.Avatar, opts Opts) (providers []auth.Provider) { +func makeAuthProviders(jwtService *auth.JWT, avatarProxy *proxy.Avatar, ds service.DataStore, opts Opts) []auth.Provider { makeParams := func(cid, secret string) auth.Params { return auth.Params{ - JwtService: jwtService, - AvatarProxy: avatarProxy, - RemarkURL: opts.RemarkURL, - Cid: cid, - Csecret: secret, - Admins: opts.Admins, - SecretKey: opts.SecretKey, + JwtService: jwtService, + AvatarProxy: avatarProxy, + RemarkURL: opts.RemarkURL, + Cid: cid, + Csecret: secret, + Admins: opts.Admins, + SecretKey: opts.SecretKey, + IsVerifiedFn: ds.IsVerifiedFn(), } } + providers := []auth.Provider{} if opts.GoogleCID != "" && opts.GoogleCSEC != "" { providers = append(providers, auth.NewGoogle(makeParams(opts.GoogleCID, opts.GoogleCSEC))) } diff --git a/app/rest/api/admin.go b/app/rest/api/admin.go index 3e353b4a..7fc22129 100644 --- a/app/rest/api/admin.go +++ b/app/rest/api/admin.go @@ -32,6 +32,7 @@ func (a *admin) routes(middlewares ...func(http.Handler) http.Handler) chi.Route router.Delete("/comment/{id}", a.deleteCommentCtrl) router.Put("/user/{userid}", a.setBlockCtrl) router.Delete("/user/{userid}", a.deleteUserCtrl) + router.Put("/verify/{userid}", a.setVerifyCtrl) router.Get("/export", a.exportCtrl) router.Put("/pin/{id}", a.setPinCtrl) router.Get("/blocked", a.blockedUsersCtrl) @@ -110,6 +111,20 @@ func (a *admin) setReadOnlyCtrl(w http.ResponseWriter, r *http.Request) { render.JSON(w, r, JSON{"locator": locator, "read-only": roStatus}) } +// PUT /verify?site=siteID&url=post-url&ro=1 - set or reset read-only status for the post +func (a *admin) setVerifyCtrl(w http.ResponseWriter, r *http.Request) { + userID := chi.URLParam(r, "userid") + siteID := r.URL.Query().Get("site") + verifyStatus := r.URL.Query().Get("verified") == "1" + + if err := a.dataService.SetVerified(siteID, userID, verifyStatus); err != nil { + rest.SendErrorJSON(w, r, http.StatusBadRequest, err, "can't set verify status") + return + } + a.cache.Flush(siteID, userID) + render.JSON(w, r, JSON{"user": userID, "verified": verifyStatus}) +} + // PUT /pin/{id}?site=siteID&url=post-url&pin=1 // mark/unmark comment as a special func (a *admin) setPinCtrl(w http.ResponseWriter, r *http.Request) { diff --git a/app/rest/api/admin_test.go b/app/rest/api/admin_test.go index 6c098825..3272db4e 100644 --- a/app/rest/api/admin_test.go +++ b/app/rest/api/admin_test.go @@ -275,6 +275,44 @@ func TestAdmin_ReadOnly(t *testing.T) { assert.False(t, info.ReadOnly) } +func TestAdmin_Verify(t *testing.T) { + srv, ts := prep(t) + assert.NotNil(t, srv) + defer cleanup(ts) + + c1 := store.Comment{Text: "test test #1", Locator: store.Locator{SiteID: "radio-t", + URL: "https://radio-t.com/blah"}, User: store.User{Name: "user1 name", ID: "user1"}} + c2 := store.Comment{Text: "test test #2", ParentID: "p1", Locator: store.Locator{SiteID: "radio-t", + URL: "https://radio-t.com/blah"}, User: store.User{Name: "user2", ID: "user2"}} + + _, err := srv.DataService.Create(c1) + assert.Nil(t, err) + _, err = srv.DataService.Create(c2) + assert.Nil(t, err) + + verified := srv.DataService.IsVerified("radio-t", "user1") + assert.False(t, verified) + + client := http.Client{} + req, err := http.NewRequest(http.MethodPut, + fmt.Sprintf("%s/api/v1/admin/verify/user1?site=radio-t&verified=1", ts.URL), nil) + assert.Nil(t, err) + withBasicAuth(req, "dev", "password") + _, err = client.Do(req) + require.Nil(t, err) + verified = srv.DataService.IsVerified("radio-t", "user1") + assert.True(t, verified) + + req, err = http.NewRequest(http.MethodPut, + fmt.Sprintf("%s/api/v1/admin/verify/user1?site=radio-t&verified=0", ts.URL), nil) + assert.Nil(t, err) + withBasicAuth(req, "dev", "password") + _, err = client.Do(req) + require.Nil(t, err) + verified = srv.DataService.IsVerified("radio-t", "user1") + assert.False(t, verified) +} + func TestAdmin_ExportStream(t *testing.T) { srv, ts := prep(t) assert.NotNil(t, srv) diff --git a/app/rest/api/middleware_test.go b/app/rest/api/middleware_test.go new file mode 100644 index 00000000..95420f0b --- /dev/null +++ b/app/rest/api/middleware_test.go @@ -0,0 +1,63 @@ +package api + +import ( + "io/ioutil" + "net/http" + "net/http/httptest" + "strings" + "testing" + + "github.com/go-chi/chi" + "github.com/stretchr/testify/assert" + "github.com/umputun/remark/app/rest" + "github.com/umputun/remark/app/store" + + "github.com/stretchr/testify/require" +) + +func TestMiddleware_AppInfo(t *testing.T) { + router := chi.NewRouter() + router.With(AppInfo("remark42", "12345")).Get("/blah", func(w http.ResponseWriter, r *http.Request) { + w.WriteHeader(200) + w.Write([]byte("blah blah")) + }) + ts := httptest.NewServer(router) + defer ts.Close() + + resp, err := http.Get(ts.URL + "/blah") + require.Nil(t, err) + assert.Equal(t, 200, resp.StatusCode) + + b, err := ioutil.ReadAll(resp.Body) + assert.NoError(t, err) + + assert.Equal(t, "blah blah", string(b)) + assert.Equal(t, "remark42", resp.Header.Get("App-Name")) + assert.Equal(t, "12345", resp.Header.Get("App-Version")) + assert.Equal(t, "Umputun", resp.Header.Get("Org")) +} + +func TestMiddleware_GetBodyAndUser(t *testing.T) { + req, err := http.NewRequest("GET", "http://example.com/request", strings.NewReader("body")) + require.Nil(t, err) + + body, user := getBodyAndUser(req, []LoggerFlag{LogAll}) + assert.Equal(t, "body", body) + assert.Equal(t, "", user, "no user") + + req = rest.SetUserInfo(req, store.User{ID: "id1", Name: "user1"}) + body, user = getBodyAndUser(req, []LoggerFlag{LogAll}) + assert.Equal(t, ` - id1 "user1"`, user, "no user") + + body, user = getBodyAndUser(req, nil) + assert.Equal(t, "", body) + assert.Equal(t, "", user, "no user") + + body, user = getBodyAndUser(req, []LoggerFlag{LogNone}) + assert.Equal(t, "", body) + assert.Equal(t, "", user, "no user") + + body, user = getBodyAndUser(req, []LoggerFlag{LogUser}) + assert.Equal(t, "", body) + assert.Equal(t, ` - id1 "user1"`, user, "no user") +} diff --git a/app/rest/api/rest.go b/app/rest/api/rest.go index c68b65ae..cbc878ad 100644 --- a/app/rest/api/rest.go +++ b/app/rest/api/rest.go @@ -128,11 +128,12 @@ func (s *Rest) routes() chi.Router { // api routes router.Route("/api/v1", func(rapi chi.Router) { - rapi.Use(Logger(LogAll), tollbooth_chi.LimitHandler(tollbooth.NewLimiter(10, nil))) + rapi.Use(tollbooth_chi.LimitHandler(tollbooth.NewLimiter(10, nil))) // open routes rapi.Group(func(ropen chi.Router) { ropen.Use(s.Authenticator.Auth(false)) + ropen.Use(Logger(LogAll)) ropen.Get("/find", s.findCommentsCtrl) ropen.Get("/id/{id}", s.commentByIDCtrl) ropen.Get("/comments", s.findUserCommentsCtrl) @@ -151,13 +152,14 @@ func (s *Rest) routes() chi.Router { // protected routes, require auth rapi.Group(func(rauth chi.Router) { rauth.Use(s.Authenticator.Auth(true)) + rauth.Use(Logger(LogAll)) rauth.Post("/comment", s.createCommentCtrl) rauth.Put("/comment/{id}", s.updateCommentCtrl) rauth.Get("/user", s.userInfoCtrl) rauth.Put("/vote/{id}", s.voteCtrl) // admin routes, admin users only - rauth.Mount("/admin", s.adminService.routes(s.Authenticator.AdminOnly)) + rauth.Mount("/admin", s.adminService.routes(s.Authenticator.AdminOnly, Logger(LogAll))) }) }) diff --git a/app/rest/auth/jwt.go b/app/rest/auth/jwt.go index 3daaa7f6..b11a3c0a 100644 --- a/app/rest/auth/jwt.go +++ b/app/rest/auth/jwt.go @@ -24,8 +24,9 @@ type CustomClaims struct { User *store.User `json:"user,omitempty"` // state and from used for oauth handshake - State string `json:"state,omitempty"` - From string `json:"from,omitempty"` + State string `json:"state,omitempty"` + From string `json:"from,omitempty"` + SiteID string `json:"site_id,omitempty"` } const jwtCookieName = "JWT" diff --git a/app/rest/auth/provider.go b/app/rest/auth/provider.go index 07275cdd..f3532967 100644 --- a/app/rest/auth/provider.go +++ b/app/rest/auth/provider.go @@ -35,13 +35,14 @@ type Provider struct { // Params to make initialized and ready to use provider type Params struct { - RemarkURL string - AvatarProxy *proxy.Avatar - JwtService *JWT - SecretKey string - Admins []string - Cid string - Csecret string + RemarkURL string + AvatarProxy *proxy.Avatar + JwtService *JWT + IsVerifiedFn func(siteID string, userID string) bool + SecretKey string + Admins []string + Cid string + Csecret string } type userData map[string]interface{} @@ -79,7 +80,7 @@ func (p Provider) Routes() chi.Router { return router } -// loginHandler - GET /login?from=redirect-back-url +// loginHandler - GET /login?from=redirect-back-url&site=siteID func (p Provider) loginHandler(w http.ResponseWriter, r *http.Request) { log.Printf("[DEBUG] login with %s", p.Name) @@ -87,8 +88,9 @@ func (p Provider) loginHandler(w http.ResponseWriter, r *http.Request) { state := p.randToken() claims := CustomClaims{ - State: state, - From: r.URL.Query().Get("from"), + State: state, + From: r.URL.Query().Get("from"), + SiteID: r.URL.Query().Get("site"), StandardClaims: jwt.StandardClaims{ Id: p.randToken(), Issuer: "remark42", @@ -166,7 +168,11 @@ func (p Provider) authHandler(w http.ResponseWriter, r *http.Request) { log.Printf("[WARN] failed to proxy avatar, %s", e) } } + u.Admin = isAdmin(u.ID, p.Admins) + if p.IsVerifiedFn != nil { + u.Verified = p.IsVerifiedFn(oauthClaims.SiteID, u.ID) + } authClaims := &CustomClaims{ User: &u, diff --git a/app/rest/auth/provider_test.go b/app/rest/auth/provider_test.go index 72017f17..4f130a94 100644 --- a/app/rest/auth/provider_test.go +++ b/app/rest/auth/provider_test.go @@ -29,7 +29,7 @@ func TestLogin(t *testing.T) { jar, err := cookiejar.New(nil) require.Nil(t, err) client := &http.Client{Jar: jar, Timeout: 5 * time.Second} - resp, err := client.Get("http://localhost:8981/login") + resp, err := client.Get("http://localhost:8981/login?site=remark") require.Nil(t, err) assert.Equal(t, 200, resp.StatusCode) body, err := ioutil.ReadAll(resp.Body) @@ -50,7 +50,7 @@ func TestLogin(t *testing.T) { Admin: false, Blocked: false, IP: ""}, u) // check admin user - resp, err = client.Get("http://localhost:8981/login") + resp, err = client.Get("http://localhost:8981/login?site=remark") assert.Nil(t, err) assert.Equal(t, 200, resp.StatusCode) body, err = ioutil.ReadAll(resp.Body) @@ -58,7 +58,7 @@ func TestLogin(t *testing.T) { err = json.Unmarshal(body, &u) assert.Nil(t, err) assert.Equal(t, store.User{Name: "blah", ID: "mock_myuser2", Picture: "http://exmple.com/pic1.png", - Admin: true, Blocked: false, IP: ""}, u) + Admin: true, Blocked: false, IP: "", Verified: true}, u) } func TestLogout(t *testing.T) { @@ -120,7 +120,8 @@ func mockProvider(t *testing.T, loginPort, authPort int) (*http.Server, *http.Se }, } params := Params{RemarkURL: "url", SecretKey: "123456", Cid: "cid", Csecret: "csecret", - JwtService: NewJWT("12345", false, time.Hour), Admins: []string{"mock_myuser2"}} + JwtService: NewJWT("12345", false, time.Hour), Admins: []string{"mock_myuser2"}, + IsVerifiedFn: func(siteID, userID string) bool { return userID == "mock_myuser2" }} provider = initProvider(params, provider) ts := &http.Server{Addr: fmt.Sprintf(":%d", loginPort), Handler: provider.Routes()} diff --git a/app/rest/user.go b/app/rest/user.go index a48a748f..ef27b171 100644 --- a/app/rest/user.go +++ b/app/rest/user.go @@ -17,7 +17,6 @@ func GetUserInfo(r *http.Request) (user store.User, err error) { if ctx == nil { return store.User{}, errors.New("no info about user") } - if u, ok := ctx.Value(contextKey("user")).(store.User); ok { return u, nil } diff --git a/app/store/engine/bolt_accessor.go b/app/store/engine/bolt_accessor.go index 17b73681..b1a94fde 100644 --- a/app/store/engine/bolt_accessor.go +++ b/app/store/engine/bolt_accessor.go @@ -35,6 +35,7 @@ const ( blocksBucketName = "block" infoBucketName = "info" readonlyBucketName = "readonly" + verifiedBucketName = "verified" // limits lastLimit = 1000 @@ -62,7 +63,7 @@ func NewBoltDB(options bolt.Options, sites ...BoltSite) (*BoltDB, error) { // make top-level buckets topBuckets := []string{postsBucketName, lastBucketName, userBucketName, blocksBucketName, - infoBucketName, readonlyBucketName} + infoBucketName, readonlyBucketName, verifiedBucketName} err = db.Update(func(tx *bolt.Tx) error { for _, bktName := range topBuckets { if _, e := tx.CreateBucketIfNotExists([]byte(bktName)); e != nil { diff --git a/app/store/engine/bolt_admin.go b/app/store/engine/bolt_admin.go index 05736ed6..8503339f 100644 --- a/app/store/engine/bolt_admin.go +++ b/app/store/engine/bolt_admin.go @@ -230,7 +230,7 @@ func (b *BoltDB) SetReadOnly(locator store.Locator, status bool) error { switch status { case true: if e := bucket.Put([]byte(locator.URL), []byte(time.Now().Format(tsNano))); e != nil { - return errors.Wrapf(e, "failed to set ro for %s to %s", locator.URL, status) + return errors.Wrapf(e, "failed to set ro for %s", locator.URL) } case false: if e := bucket.Delete([]byte(locator.URL)); e != nil { @@ -256,3 +256,42 @@ func (b *BoltDB) IsReadOnly(locator store.Locator) (ro bool) { }) return ro } + +// SetVerified makes user verified or reset the flag +func (b *BoltDB) SetVerified(siteID string, userID string, status bool) error { + bdb, err := b.db(siteID) + if err != nil { + return err + } + + return bdb.Update(func(tx *bolt.Tx) error { + bucket := tx.Bucket([]byte(verifiedBucketName)) + switch status { + case true: + if e := bucket.Put([]byte(userID), []byte(time.Now().Format(tsNano))); e != nil { + return errors.Wrapf(e, "failed to set verified status for %s", userID) + } + case false: + if e := bucket.Delete([]byte(userID)); e != nil { + return errors.Wrapf(e, "failed to clean verified status for %s", userID) + } + } + return nil + }) +} + +// IsVerified checks if user verified +func (b *BoltDB) IsVerified(siteID string, userID string) (verified bool) { + + bdb, err := b.db(siteID) + if err != nil { + return false + } + + _ = bdb.View(func(tx *bolt.Tx) error { + bucket := tx.Bucket([]byte(verifiedBucketName)) + verified = bucket.Get([]byte(userID)) != nil + return nil + }) + return verified +} diff --git a/app/store/engine/bolt_admin_test.go b/app/store/engine/bolt_admin_test.go index 75dd2b74..94e64d94 100644 --- a/app/store/engine/bolt_admin_test.go +++ b/app/store/engine/bolt_admin_test.go @@ -179,3 +179,22 @@ func TestBoltAdmin_ReadOnly(t *testing.T) { assert.False(t, b.IsReadOnly(store.Locator{SiteID: "radio-t-bad", URL: "url-1"}), "nothing blocked on wrong site") } + +func TestBoltAdmin_Verified(t *testing.T) { + defer os.Remove(testDb) + b := prep(t) + + assert.False(t, b.IsVerified("radio-t", "u1"), "nothing verified") + + assert.NoError(t, b.SetVerified("radio-t", "u1", true)) + assert.True(t, b.IsVerified("radio-t", "u1"), "u1 verified") + + assert.False(t, b.IsVerified("radio-t", "u2"), "u2 still not verified") + assert.NoError(t, b.SetVerified("radio-t", "u1", false)) + assert.False(t, b.IsVerified("radio-t", "u1"), "u1 not verified anymore") + + assert.EqualError(t, b.SetVerified("bad", "u1", true), `site "bad" not found`) + assert.NoError(t, b.SetVerified("radio-t", "u1xyz", false)) + + assert.False(t, b.IsVerified("radio-t-bad", "u1"), "nothing verified on wrong site") +} diff --git a/app/store/engine/engine.go b/app/store/engine/engine.go index c8f350c3..73c94e0d 100644 --- a/app/store/engine/engine.go +++ b/app/store/engine/engine.go @@ -40,6 +40,8 @@ type Admin interface { Blocked(siteID string) ([]store.BlockedUser, error) // get list of blocked users SetReadOnly(locator store.Locator, status bool) error // set/reset read-only flag IsReadOnly(locator store.Locator) bool // check if post read-only + SetVerified(siteID string, userID string, status bool) error // set/reset verified flag + IsVerified(siteID string, userID string) bool // check verified status } // sortComments is for engines can't sort data internally diff --git a/app/store/service/service.go b/app/store/service/service.go index 794f7bd2..b2b3c47d 100644 --- a/app/store/service/service.go +++ b/app/store/service/service.go @@ -150,3 +150,13 @@ func (s *DataStore) ValidateComment(c *store.Comment) error { } return nil } + +// IsVerifiedFn returns func to check if user verified or not +func (s *DataStore) IsVerifiedFn() func(siteID string, userID string) bool { + return func(siteID string, userID string) bool { + if siteID == "" { + return false + } + return s.IsVerified(siteID, userID) + } +} diff --git a/app/store/user.go b/app/store/user.go index f36ac3fd..6dbe7a63 100644 --- a/app/store/user.go +++ b/app/store/user.go @@ -11,12 +11,13 @@ import ( // User holds user-related info type User struct { - Name string `json:"name"` - ID string `json:"id"` - Picture string `json:"picture"` - Admin bool `json:"admin"` - Blocked bool `json:"block,omitempty"` - IP string `json:"ip,omitempty"` + Name string `json:"name"` + ID string `json:"id"` + Picture string `json:"picture"` + Admin bool `json:"admin"` + Blocked bool `json:"block,omitempty"` + IP string `json:"ip,omitempty"` + Verified bool `json:"verified,omitempty"` } var reValidSha = regexp.MustCompile("^[a-fA-F0-9]{40}$") diff --git a/remark.rest b/remark.rest index 42aaceef..2efe5dc4 100644 --- a/remark.rest +++ b/remark.rest @@ -1,12 +1,12 @@ ### find request with tree -GET {{host}}/api/v1/find?site=radiot&sort=-active&format=tree&url=https://radio-t.com/p/2018/05/05/podcast-596/ +GET {{host}}/api/v1/find?site={{site}}&sort=-active&format=tree&url={{url}} ### find request with plain -GET {{host}}/api/v1/find?site=radiot&sort=-time&format=plain&url=https://radio-t.com/p/2018/05/08/prep-597/ +GET {{host}}/api/v1/find?site={{site}}&sort=-time&format=plain&url={{url}} ### last 50 comments -GET {{host}}/api/v1/last/50?site=remark +GET {{host}}/api/v1/last/50?site={{site}} ### create comment POST {{host}}/api/v1/comment @@ -15,8 +15,8 @@ Content-Type: application/json { "text": "comment *blah* http://radio-t.com", "locator": { - "url": "https://radio-t.com/blah1", - "site": "remark" + "url": "{{url}}", + "site": "{{site}}" } } @@ -27,8 +27,8 @@ Content-Type: application/json { "text": "comment *blah* http://radio-t.com ", "locator": { - "url": "https://radio-t.com/blah1", - "site": "remark" + "url": "{{url}}", + "site": "{{site}}" } } @@ -50,28 +50,28 @@ Content-Type: application/json } ### pin comment -PUT {{host}}/api/v1/admin/pin/3665976683?site=remark&url=https://remark42.com/demo/&pin=1 +PUT {{host}}/api/v1/admin/pin/3665976683?site=remark&url={{url}}&pin=1 ### vote for comment -PUT {{host}}/api/v1/vote/3665976683?site=remark&url=https://remark42.com/demo/&vote=1 +PUT {{host}}/api/v1/vote/3665976683?site=remark&url={{url}}&vote=1 ### get user info GET {{host}}/api/v1/user ### get comment by id -GET {{host}}/api/v1/id/3665976683?site=remark&url=https://remark42.com/demo/ +GET {{host}}/api/v1/id/3665976683?site=remark&url={{url}} ### get comment by id 2 GET {{host}}/api/v1/id/a2ddb8d2f65008ee1a1e3af8df0f26beb042309c?site=remark&url=https://radio-t.com/blah1 ### get comment by user id -GET {{host}}/api/v1/comments?site=remark&user=disqus_umputun&limit=5 +GET {{host}}/api/v1/comments?site={{site}}&user=github_ef0f706a79cc24b17bbbb374cd234a691d034128&limit=5 ### get comment by user id2 GET {{host}}/api/v1/comments?site=remark&user=disqus_kpmy ### get count -GET {{host}}/api/v1/count?site=remark&url=https://remark42.com/demo/ +GET {{host}}/api/v1/count?site=remark&url={{url}} ### get counts for many POST {{host}}/api/v1/counts?site=remark @@ -80,11 +80,11 @@ Content-Type: application/json [ "https://radio-t.com/p/2017/12/02/podcast-574/", "https://radio-t.com/p/2017/12/09/podcast-575/", - "https://remark42.com/demo/" + "{{url}}" ] ### list commented posts -GET {{host}}/api/v1/list?site=radiot&limit=10&skip=5 +GET {{host}}/api/v1/list?site={{site}}&limit=10&skip=5 ### get config GET {{host}}/api/v1/config @@ -96,16 +96,16 @@ PUT {{host}}/api/v1/admin/user/disqus_grigorybakunov?site=remark&block=1 PUT {{host}}/api/v1/admin/user/disqus_grigorybakunov?site=remark&block=0 ### list blocked user -GET {{host}}/api/v1/admin/blocked?site=remark +GET {{host}}/api/v1/admin/blocked?site={{site}} ### delete comment by id -DELETE {{host}}/api/v1/admin/comment/3665976683?site=remark&url=https://remark42.com/demo/ +DELETE {{host}}/api/v1/admin/comment/3665976683?site=remark&url={{url}} ### get post info -GET {{host}}/api/v1/info?site=radiot&url=https://radio-t.com/p/2018/05/08/prep-597/ +GET {{host}}/api/v1/info?site={{site}}&url=https://radio-t.com/p/2018/05/08/prep-597/ ### post rss -GET {{host}}/api/v1/rss/post?site=remark&url=https://remark42.com/demo/ +GET {{host}}/api/v1/rss/post?site=remark&url={{url}} ### site rss PUT {{host}}/api/v1/rss/site?site=remark diff --git a/scripts/create-backup.sh b/scripts/create-backup.sh index b05f35c9..751007d8 100755 --- a/scripts/create-backup.sh +++ b/scripts/create-backup.sh @@ -1,7 +1,7 @@ #!/bin/sh # this scrips makes a backup file to /srv/var/userbackup--.gz - +set -e BACKUP_PATH=${BACKUP_PATH:-./var} backup_file=${BACKUP_PATH}/userbackup-${1}-$(date +%s).gz echo "make backup file for site $1 to $backup_file" diff --git a/scripts/import-disqus.sh b/scripts/import-disqus.sh index 8217fb39..e400f71e 100644 --- a/scripts/import-disqus.sh +++ b/scripts/import-disqus.sh @@ -1,3 +1,4 @@ #!/bin/sh +set -e echo "import disqus file $1 to site $2" curl -X POST -H "Content-Type: application/json" -d @/srv/var/$1 "http://127.0.0.1:8081/api/v1/admin/import?site=${2}&provider=disqus&secret=${SECRET}" diff --git a/scripts/migrate-data.sh b/scripts/migrate-data.sh index d69c3a9f..6b6c2347 100755 --- a/scripts/migrate-data.sh +++ b/scripts/migrate-data.sh @@ -2,7 +2,7 @@ # this scrips making a backup file to /tmp/export-remark.gz and loading it back # useful to migrate data schema in case if new version of data store incomaptible with the stored comments. - +set -e echo "make backup file for site $1" curl "http://127.0.0.1:8081/api/v1/admin/export?site=${1}&secret=${SECRET}" > /tmp/export-remark.gz @@ -19,5 +19,5 @@ ls -laH /tmp/backup.remark echo "export to site $1" curl -X POST -H "Content-Type: application/json" --data-binary @/tmp/backup.remark "http://127.0.0.1:8081/api/v1/admin/import?site=${1}&provider=native&secret=${SECRET}" -rm -f /tmp/backup.remark -rm -f /tmp/export-remark.gz +rm /tmp/backup.remark +rm /tmp/export-remark.gz diff --git a/scripts/restore-backup.sh b/scripts/restore-backup.sh index 12dc909f..8284006c 100644 --- a/scripts/restore-backup.sh +++ b/scripts/restore-backup.sh @@ -1,4 +1,5 @@ #!/bin/sh +set -e echo "import backup file $1 to site $2" echo "unpack $1" @@ -8,4 +9,4 @@ size=`stat -c "%s" /tmp/backup.remark` echo "source file size ${size}" curl -X POST -H "Content-Type: application/json" --data-binary @/tmp/backup.remark "http://127.0.0.1:8081/api/v1/admin/import?site=${2}&provider=native&secret=${SECRET}" -rm -fq /tmp/backup.remark +rm /tmp/backup.remark