From f61051c212d60a47577754a0141cd405e1907f03 Mon Sep 17 00:00:00 2001 From: Knut Ahlers Date: Sun, 12 Jan 2020 13:37:20 +0100 Subject: [PATCH] Add tweet deletion, improve API Signed-off-by: Knut Ahlers --- frontend/app.js | 23 ++++++++++++++--- frontend/index.html | 3 +++ go.mod | 1 + go.sum | 2 ++ http.go | 62 +++++++++++++++++++++++++++------------------ main.go | 6 +++-- 6 files changed, 68 insertions(+), 29 deletions(-) diff --git a/frontend/app.js b/frontend/app.js index bb9a30c..dac154b 100644 --- a/frontend/app.js +++ b/frontend/app.js @@ -25,9 +25,26 @@ new Vue({ this.modalTweet = tweet }, + deleteTweet(tweet) { + axios + .delete(`/api/${tweet.id}`) + .then(() => { + const tweets = [] + + for (const t in this.tweets) { + if (t.id !== tweet.id) { + tweets.push(t) + } + } + + this.tweets = tweets + }) + .catch(err => console.log(err)) + }, + favourite(tweet) { axios - .post('/api/favourite', { id: tweet.id }) + .put(`/api/${tweet.id}/favorite`) .then(res => { if (res.data.length === 0) { this.refetch(tweet) @@ -49,7 +66,7 @@ new Vue({ refetch(tweet) { axios - .post('/api/refresh', { id: tweet.id }) + .put(`/api/${tweet.id}/refresh`) .then(res => { if (res.data.length === 0) { return @@ -82,7 +99,7 @@ new Vue({ triggerForceFetch() { axios - .post('/api/force-reload') + .put('/api/force-reload') .then(() => { this.notify('Force refresh triggered, reloading tweets in 10s') window.setTimeout(() => this.refresh(true), 10000) diff --git a/frontend/index.html b/frontend/index.html index db1f22a..f2c70f1 100644 --- a/frontend/index.html +++ b/frontend/index.html @@ -63,6 +63,9 @@ {{ tweet.images.length }} + + + diff --git a/go.mod b/go.mod index f35ce1a..76b5734 100644 --- a/go.mod +++ b/go.mod @@ -11,6 +11,7 @@ require ( github.com/dustin/go-jsonpointer v0.0.0-20160814072949-ba0abeacc3dc // indirect github.com/dustin/gojson v0.0.0-20160307161227-2e71ec9dd5ad // indirect github.com/garyburd/go-oauth v0.0.0-20180319155456-bca2e7f09a17 // indirect + github.com/gorilla/mux v1.7.3 github.com/konsorten/go-windows-terminal-sequences v1.0.2 // indirect github.com/pkg/errors v0.8.1 github.com/sirupsen/logrus v1.4.1 diff --git a/go.sum b/go.sum index 793fe36..dab706b 100644 --- a/go.sum +++ b/go.sum @@ -17,6 +17,8 @@ github.com/dustin/gojson v0.0.0-20160307161227-2e71ec9dd5ad h1:Qk76DOWdOp+GlyDKB github.com/dustin/gojson v0.0.0-20160307161227-2e71ec9dd5ad/go.mod h1:mPKfmRa823oBIgl2r20LeMSpTAteW5j7FLkc0vjmzyQ= github.com/garyburd/go-oauth v0.0.0-20180319155456-bca2e7f09a17 h1:GOfMz6cRgTJ9jWV0qAezv642OhPnKEG7gtUjJSdStHE= github.com/garyburd/go-oauth v0.0.0-20180319155456-bca2e7f09a17/go.mod h1:HfkOCN6fkKKaPSAeNq/er3xObxTW4VLeY6UUK895gLQ= +github.com/gorilla/mux v1.7.3 h1:gnP5JzjVOuiZD07fKKToCAOjS0yOpj/qPETTXCCS6hw= +github.com/gorilla/mux v1.7.3/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs= github.com/inconshreveable/go-update v0.0.0-20160112193335-8152e7eb6ccf/go.mod h1:hyb9oH7vZsitZCiBt0ZvifOrB+qc8PS5IiilCIb87rg= github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= github.com/konsorten/go-windows-terminal-sequences v1.0.2/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= diff --git a/http.go b/http.go index 3a6cf3b..8a6770e 100644 --- a/http.go +++ b/http.go @@ -8,29 +8,49 @@ import ( "strings" "github.com/ChimeraCoder/anaconda" + "github.com/gorilla/mux" + "github.com/pkg/errors" log "github.com/sirupsen/logrus" ) func init() { - http.HandleFunc("/api/favourite", handleFavorite) - http.HandleFunc("/api/force-reload", handleForceReload) - http.HandleFunc("/api/page", handlePage) - http.HandleFunc("/api/refresh", handleTweetRefresh) - http.HandleFunc("/api/since", handleNewest) + router.HandleFunc("/api/{tweetID}/favorite", handleFavorite).Methods(http.MethodPut) + router.HandleFunc("/api/{tweetID}", handleDelete).Methods(http.MethodDelete) + router.HandleFunc("/api/force-reload", handleForceReload).Methods(http.MethodPut) + router.HandleFunc("/api/page", handlePage).Methods(http.MethodGet) + router.HandleFunc("/api/{tweetID}/refresh", handleTweetRefresh).Methods(http.MethodPut) + router.HandleFunc("/api/since", handleNewest).Methods(http.MethodGet) +} + +func handleDelete(w http.ResponseWriter, r *http.Request) { + var vars = mux.Vars(r) + + tweetID, err := strconv.ParseInt(vars["tweetID"], 10, 64) + if err != nil { + http.Error(w, errors.Wrap(err, "Unable to parse TweetID").Error(), http.StatusBadRequest) + return + } + + if err := tweetStore.DeleteTweetByID(uint64(tweetID)); err != nil { + log.WithError(err).Error("Unable to delete tweet") + http.Error(w, "Something went wrong", http.StatusInternalServerError) + return + } + + w.WriteHeader(http.StatusNoContent) } // handleFavorite takes the ID of a tweet and submits a favorite to Twitter func handleFavorite(w http.ResponseWriter, r *http.Request) { - req := struct { - ID int64 `json:"id,string"` - }{} + var vars = mux.Vars(r) - if err := json.NewDecoder(r.Body).Decode(&req); err != nil || req.ID == 0 { - http.Error(w, "Need to specify id", http.StatusBadRequest) + tweetID, err := strconv.ParseInt(vars["tweetID"], 10, 64) + if err != nil { + http.Error(w, errors.Wrap(err, "Unable to parse TweetID").Error(), http.StatusBadRequest) return } - tweet, err := twitter.Favorite(req.ID) + tweet, err := twitter.Favorite(tweetID) if err != nil { log.WithError(err).Error("Unable to favourite tweet") http.Error(w, "Something went wrong", http.StatusInternalServerError) @@ -55,11 +75,6 @@ func handleFavorite(w http.ResponseWriter, r *http.Request) { // handleForceReload issues a full load of the latest tweets to update their state func handleForceReload(w http.ResponseWriter, r *http.Request) { - if r.Method != http.MethodPost { - http.Error(w, "This needs to be POST", http.StatusBadRequest) - return - } - go loadAndStoreTweets(true) w.WriteHeader(http.StatusNoContent) @@ -94,20 +109,19 @@ func handlePage(w http.ResponseWriter, r *http.Request) { // handleTweetRefresh refreshes the state of the tweet with the given ID against the Twitter API func handleTweetRefresh(w http.ResponseWriter, r *http.Request) { - req := struct { - ID int64 `json:"id,string"` - }{} + var vars = mux.Vars(r) - if err := json.NewDecoder(r.Body).Decode(&req); err != nil || req.ID == 0 { - http.Error(w, "Need to specify id", http.StatusBadRequest) + tweetID, err := strconv.ParseInt(vars["tweetID"], 10, 64) + if err != nil { + http.Error(w, errors.Wrap(err, "Unable to parse TweetID").Error(), http.StatusBadRequest) return } - tweet, err := twitter.GetTweet(req.ID, url.Values{}) + tweet, err := twitter.GetTweet(tweetID, url.Values{}) if err != nil { if strings.Contains(err.Error(), "No status found with that ID.") { - log.WithField("id", req.ID).Info("Removing no longer existing tweet") - if err = tweetStore.DeleteTweetByID(uint64(req.ID)); err != nil { + log.WithField("id", tweetID).Info("Removing no longer existing tweet") + if err = tweetStore.DeleteTweetByID(uint64(tweetID)); err != nil { log.WithError(err).Error("Unable to delete tweet") http.Error(w, "Something went wrong", http.StatusInternalServerError) } diff --git a/main.go b/main.go index 2d75c18..18bae30 100644 --- a/main.go +++ b/main.go @@ -9,6 +9,7 @@ import ( "time" "github.com/ChimeraCoder/anaconda" + "github.com/gorilla/mux" log "github.com/sirupsen/logrus" hhelp "github.com/Luzifer/go_helpers/v2/http" @@ -30,6 +31,7 @@ var ( VersionAndExit bool `flag:"version" default:"false" description:"Prints current version and exits"` }{} + router = mux.NewRouter() tweetStore *store twitter *anaconda.TwitterApi @@ -72,8 +74,8 @@ func main() { log.WithField("version", version).Info("MediaTimeline Viewer started") - http.Handle("/", http.FileServer(http.Dir(cfg.Frontend))) - http.ListenAndServe(cfg.Listen, hhelp.NewHTTPLogHandler(http.DefaultServeMux)) + router.Handle("/", http.FileServer(http.Dir(cfg.Frontend))) + http.ListenAndServe(cfg.Listen, hhelp.NewHTTPLogHandler(router)) } func loadAndStoreTweets(forceRefresh bool) {