From 3b397300bca2d1b75249f7138bbd0e3635e5ebc5 Mon Sep 17 00:00:00 2001 From: Lucas BEE Date: Fri, 21 Jun 2019 09:04:48 +0000 Subject: [PATCH] Add the Eventers logic and handlers --- backend/admins/users.go | 55 +++++--- backend/auth/middleware.go | 38 ++++++ backend/events/channel.go | 80 ++++++++++-- backend/events/handlers.go | 62 +++++++-- backend/events/notifier.go | 101 ++------------- backend/events/polochons.go | 193 ++++++++++++++++++++++++++++ backend/events/torrent_eventer.go | 128 ++++++++++++++++++ backend/events/torrents.go | 92 ------------- backend/events/video_eventer.go | 41 ++++++ backend/external_medias/handlers.go | 4 +- backend/main.go | 4 +- backend/models/polochons.go | 40 +++--- backend/models/users.go | 71 ++-------- backend/movies/handlers.go | 20 +-- backend/movies/movies.go | 2 +- backend/polochons/handlers.go | 70 +++++++--- backend/routes.go | 11 +- backend/shows/handlers.go | 14 +- backend/shows/shows.go | 2 +- backend/torrents/handlers.go | 6 +- backend/users/handlers.go | 18 +-- go.mod | 3 + go.sum | 3 + 23 files changed, 697 insertions(+), 361 deletions(-) create mode 100644 backend/events/polochons.go create mode 100644 backend/events/torrent_eventer.go delete mode 100644 backend/events/torrents.go create mode 100644 backend/events/video_eventer.go diff --git a/backend/admins/users.go b/backend/admins/users.go index 406f259..3fc4291 100644 --- a/backend/admins/users.go +++ b/backend/admins/users.go @@ -1,11 +1,12 @@ package admin import ( + "database/sql" "encoding/json" "fmt" "net/http" - "git.quimbo.fr/odwrtw/canape/backend/config" + "git.quimbo.fr/odwrtw/canape/backend/events" "git.quimbo.fr/odwrtw/canape/backend/models" "git.quimbo.fr/odwrtw/canape/backend/web" @@ -21,11 +22,32 @@ func GetUsersHandler(env *web.Env, w http.ResponseWriter, r *http.Request) error log.Debug("Getting users") + // Get all users users, err := models.GetAllUsers(env.Database) if err != nil { return env.RenderError(w, err) } + // Get all polochons + polochons, err := models.GetAllPolochons(env.Database) + if err != nil { + return env.RenderError(w, err) + } + + // Map the polochons + polochonMap := map[string]*models.Polochon{} + for _, p := range polochons { + polochonMap[p.ID] = p + } + + // Fill the users with the polochons + for _, u := range users { + if u.PolochonID.Valid == false { + continue + } + u.Polochon = polochonMap[u.PolochonID.String] + } + return env.RenderJSON(w, users) } @@ -64,12 +86,13 @@ func UpdateUserHandler(env *web.Env, w http.ResponseWriter, r *http.Request) err }) var data struct { - ID string `json:"userId"` - Admin bool `json:"admin"` - Activated bool `json:"activated"` - PolochonURL string `json:"polochonUrl"` - PolochonToken string `json:"polochonToken"` - Password string `json:"password"` + ID string `json:"userId"` + Admin bool `json:"admin"` + Activated bool `json:"activated"` + PolochonID string `json:"polochonId"` + PolochonToken string `json:"polochonToken"` + PolochonActivated bool `json:"polochonActivated"` + Password string `json:"password"` } if err := json.NewDecoder(r.Body).Decode(&data); err != nil { return err @@ -84,17 +107,14 @@ func UpdateUserHandler(env *web.Env, w http.ResponseWriter, r *http.Request) err return err } - // Update the polochon config - polochonConfig := &config.UserPolochon{ - URL: data.PolochonURL, - Token: data.PolochonToken, - } - if err := user.SetConfig("polochon", polochonConfig); err != nil { - return err - } - user.Admin = data.Admin user.Activated = data.Activated + user.PolochonActivated = data.PolochonActivated + user.PolochonID = sql.NullString{ + String: data.PolochonID, + Valid: true, + } + user.Token = data.PolochonToken if data.Password != "" { // Update the user config @@ -107,6 +127,9 @@ func UpdateUserHandler(env *web.Env, w http.ResponseWriter, r *http.Request) err log.Debugf("updating user") + // Need to unsubscribe the user from all the eventers + events.Unsubscribe(user) + // Save the user with the new configurations if err := user.Update(env.Database); err != nil { return err diff --git a/backend/auth/middleware.go b/backend/auth/middleware.go index 3638620..67f4873 100644 --- a/backend/auth/middleware.go +++ b/backend/auth/middleware.go @@ -6,10 +6,12 @@ import ( "net/http" "git.quimbo.fr/odwrtw/canape/backend/models" + "github.com/gofrs/uuid" "github.com/sirupsen/logrus" ) type ipContextKey string +type requestIDContextKey string type authContextKey string // Middleware get User from session and put it in context @@ -158,3 +160,39 @@ func getRequestIP(req *http.Request) string { func getIPFromRequest(r *http.Request) string { return r.Context().Value(ipContextKey("ip")).(string) } + +// RequestIDMiddleware set the request ID in the request context +type RequestIDMiddleware struct { + log *logrus.Entry +} + +//TODO: Move this somewhere else + +// NewRequestIDMiddleware returns a new requestID middleware +func NewRequestIDMiddleware(log *logrus.Entry) *RequestIDMiddleware { + return &RequestIDMiddleware{ + log: log.WithField("middleware", "request_id"), + } +} + +func (m *RequestIDMiddleware) ServeHTTP(w http.ResponseWriter, r *http.Request, next http.HandlerFunc) { + ctxKey := requestIDContextKey("request_id") + reqID, err := uuid.NewV4() + if err == nil { + ctx := context.WithValue(r.Context(), ctxKey, reqID.String()) + r = r.WithContext(ctx) + r.Header.Set("X-Request-Id", reqID.String()) + w.Header().Set("X-Request-Id", reqID.String()) + } + + next(w, r) +} + +// GetRequestIDFromRequest returns the request id from the request +func GetRequestIDFromRequest(r *http.Request) string { + req := r.Context().Value(requestIDContextKey("request_id")) + if req != nil { + return req.(string) + } + return "" +} diff --git a/backend/events/channel.go b/backend/events/channel.go index be15a9e..f3d3830 100644 --- a/backend/events/channel.go +++ b/backend/events/channel.go @@ -4,13 +4,15 @@ import ( "net" "time" + "git.quimbo.fr/odwrtw/canape/backend/models" "github.com/gorilla/websocket" + "github.com/jmoiron/sqlx" "github.com/sirupsen/logrus" ) -type channel struct { - log *logrus.Entry - events map[string]Eventer +// Channel represents the channel of the user and the server +type Channel struct { + log *logrus.Entry // Channel where the eventer will write messages to serverEventStream chan ServerEvent // Channel where the eventer will write errors to @@ -19,10 +21,16 @@ type channel struct { done chan struct{} // Underlying ws connection conn *websocket.Conn + // A channel is directly linked to a user + User *models.User + // List of events the user is listening to + events map[string]struct{} + db *sqlx.DB + id string } // go routine writing events to the websocket connection -func (c *channel) writer() { +func (c *Channel) writer() { // Create the ping timer that will ping the client every pingWait seconds // to check that he's still listening pingTicker := time.NewTicker(pingWait) @@ -60,7 +68,7 @@ func (c *channel) writer() { } // go routine reading messages from the websocket connection -func (c *channel) reader() { +func (c *Channel) reader() { // Read loop c.conn.SetReadDeadline(time.Now().Add(pongWait)) for { @@ -93,7 +101,7 @@ func (c *channel) reader() { return } - e, ok := c.events[msg.Message] + e, ok := Eventers[msg.Message] if !ok { c.log.Warnf("no such event to subscribe %q", msg.Message) continue @@ -102,21 +110,71 @@ func (c *channel) reader() { switch msg.Type { case "subscribe": c.log.Debugf("subscribe to %s", msg.Message) - e.Subscribe(e.Launch) + if _, ok := c.events[e.Name]; ok { + c.log.Infof("user %s is already subscribed to %s", c.User.Name, e.Name) + continue + } + if err := e.Subscribe(c); err != nil { + c.Error(e.Name, err) + continue + } + c.events[e.Name] = struct{}{} case "unsubscribe": c.log.Debugf("unsubscribe from %s", msg.Message) - e.Unsubscribe() + if _, ok := c.events[e.Name]; !ok { + c.log.Infof("user %s is not subscribed to %s", c.User.Name, e.Name) + continue + } + e.Unsubscribe(c) default: c.log.Warnf("invalid type: %s", msg.Type) } } } -func (c *channel) close() { +func (c *Channel) close() { // Unsubscribe from all events - for _, e := range c.events { - e.Unsubscribe() + for eventName := range c.events { + Eventers[eventName].Unsubscribe(c) } // Tell the writer to stop c.done <- struct{}{} } + +// Error sends an error into the errorStream channel +func (c *Channel) Error(name string, err error) { + c.log.WithField("name", name).Warn(err) + c.serverErrorStream <- ServerError{ + Event: Event{ + Type: name, + Status: Error, + }, + ErrorEvent: ErrorEvent{ + Level: WarningError, + Message: err.Error(), + }, + } +} + +// FatalError sends an error into the errorStream channel +func (c *Channel) FatalError(name string, err error) { + c.log.WithField("name", name).Warn(err) + c.serverErrorStream <- ServerError{ + Event: Event{ + Type: name, + Status: Error, + }, + ErrorEvent: ErrorEvent{ + Level: FatalError, + Message: err.Error(), + }, + } +} + +// Unsubscribe unsubscribes a user from all eventers +func Unsubscribe(u *models.User) { + chanl := &Channel{User: u} + for _, event := range Eventers { + event.Unsubscribe(chanl) + } +} diff --git a/backend/events/handlers.go b/backend/events/handlers.go index a982ed6..a495787 100644 --- a/backend/events/handlers.go +++ b/backend/events/handlers.go @@ -1,11 +1,15 @@ package events import ( + "encoding/json" + "fmt" "net/http" "time" "git.quimbo.fr/odwrtw/canape/backend/auth" + "git.quimbo.fr/odwrtw/canape/backend/models" "git.quimbo.fr/odwrtw/canape/backend/web" + "github.com/gorilla/mux" "github.com/gorilla/websocket" ) @@ -18,6 +22,12 @@ const ( writeWait = 30 * time.Second ) +// Eventers is a map of all the available Eventers +var Eventers = map[string]*PolochonEventers{ + torrentEventName: NewTorrentEventers(), + videoEventName: NewVideoEventers(), +} + // WsHandler handles the websockets messages func WsHandler(env *web.Env, w http.ResponseWriter, r *http.Request) error { // Get the user @@ -50,21 +60,18 @@ func WsHandler(env *web.Env, w http.ResponseWriter, r *http.Request) error { // Channel where the eventers will write errors to serverErrorStream := make(chan ServerError) - events := map[string]Eventer{ - "torrents": &TorrentNotifier{ - Notifier: NewNotifier(serverEventStream, serverErrorStream), - user: user, - log: env.Log, - }, - } + requestID := auth.GetRequestIDFromRequest(r) - c := channel{ + c := Channel{ serverEventStream: serverEventStream, serverErrorStream: serverErrorStream, done: make(chan struct{}, 1), conn: ws, log: env.Log, - events: events, + User: user, + db: env.Database, + events: map[string]struct{}{}, + id: requestID, } // Launch the go routine responsible for writing events in the websocket @@ -74,3 +81,40 @@ func WsHandler(env *web.Env, w http.ResponseWriter, r *http.Request) error { return nil } + +// PolochonHookHandler handles the websockets messages +func PolochonHookHandler(env *web.Env, w http.ResponseWriter, r *http.Request) error { + vars := mux.Vars(r) + id := vars["id"] + token := r.URL.Query().Get("token") + env.Log.Infof("Got call on hook with %s and %s", id, token) + + // Get the polochon associated + p, err := models.GetPolochonByID(env.Database, id) + if err != nil { + return env.RenderError(w, err) + } + + // Check the auth + if token != p.AuthToken { + return env.RenderError(w, fmt.Errorf("Forbidden")) + } + + e := Eventers[videoEventName] + notifier, ok := e.polochons[id] + if !ok { + env.Log.Infof("nobody is listening") + return env.RenderOK(w, "nobody is listening") + } + + var data struct { + Type string `json:"type"` + Data interface{} `json:"data"` + } + if err := json.NewDecoder(r.Body).Decode(&data); err != nil { + return err + } + notifier.NotifyAll(data) + + return env.RenderOK(w, "All good") +} diff --git a/backend/events/notifier.go b/backend/events/notifier.go index a7d9f38..648443e 100644 --- a/backend/events/notifier.go +++ b/backend/events/notifier.go @@ -1,9 +1,5 @@ package events -import ( - "sync" -) - // ErrorLevel is the level of the ServerError type ErrorLevel string @@ -16,8 +12,9 @@ const ( WarningError ErrorLevel = "warning" FatalError ErrorLevel = "fatal" // Statuses - OK Status = "ok" - Error Status = "error" + OK Status = "ok" + Disconnected Status = "disconnected" + Error Status = "error" ) // Event is the base of a message @@ -52,90 +49,10 @@ type ServerError struct { // Eventer define an interface that any eventer must follow type Eventer interface { - Subscribe(func()) - Unsubscribe() - Launch() - Name() string -} - -// Notifier represents the base of any Eventer -// Notify implements Subscribe and Unsubscribe methods -type Notifier struct { - sync.RWMutex - subscribed bool - done chan struct{} - eventStream chan ServerEvent - errorStream chan ServerError -} - -// NewNotifier returns a new notifier -func NewNotifier(eventStream chan ServerEvent, errorStream chan ServerError) *Notifier { - return &Notifier{ - eventStream: eventStream, - errorStream: errorStream, - done: make(chan struct{}), - } -} - -// Subscribe subscribes a client on the Notifier -func (t *Notifier) Subscribe(launch func()) { - t.Lock() - defer t.Unlock() - - // Check if already subscribed - if t.subscribed { - return - } - t.subscribed = true - - go launch() -} - -// Unsubscribe unsubscribes a client from receiving any more events from -// the Notifier -func (t *Notifier) Unsubscribe() { - t.Lock() - defer t.Unlock() - - // Check if already unsubscribed - if !t.subscribed { - return - } - t.subscribed = false - - t.done <- struct{}{} -} - -// Error sends an error into the errorStream channel -func (t *Notifier) Error(name string, err error) { - t.errorStream <- ServerError{ - Event: Event{ - Type: name, - Status: Error, - }, - ErrorEvent: ErrorEvent{ - Level: WarningError, - Message: err.Error(), - }, - } -} - -// FatalError sends an error into the errorStream channel and tell the client -// that the error is fatal, the notifier won't communicate until a new -// subscribe -func (t *Notifier) FatalError(name string, err error) { - t.Lock() - defer t.Unlock() - - t.errorStream <- ServerError{ - Event: Event{ - Type: name, - Status: Error, - }, - ErrorEvent: ErrorEvent{ - Level: FatalError, - Message: err.Error(), - }, - } - t.subscribed = false + Unsubscribe(chanl *Channel) error + Append(chanl *Channel) + Subscribers() []*Channel + NotifyAll(data interface{}) + Launch() error + Finish() } diff --git a/backend/events/polochons.go b/backend/events/polochons.go new file mode 100644 index 0000000..b5c3525 --- /dev/null +++ b/backend/events/polochons.go @@ -0,0 +1,193 @@ +package events + +import ( + "fmt" + "sync" + + "git.quimbo.fr/odwrtw/canape/backend/models" + "github.com/sirupsen/logrus" +) + +// BaseEventer represents the basis of a Eventer +type BaseEventer struct { + users []*Channel + log *logrus.Entry + name string +} + +// PolochonEventers represents the basis of Eventers +// It is mainly composed of a map of polochons, and a Method to create new +// Eventer per polochon +type PolochonEventers struct { + sync.RWMutex + Name string + log *logrus.Entry + polochons map[string]Eventer + NewEventer func(polo *models.Polochon, log *logrus.Entry) (Eventer, error) +} + +// NewEventers returns a new PolochonEventers +func NewEventers() *PolochonEventers { + // Setup the logger + logger := logrus.New() + logger.Formatter = &logrus.TextFormatter{FullTimestamp: true} + logger.Level = logrus.DebugLevel + + return &PolochonEventers{ + polochons: map[string]Eventer{}, + log: logrus.NewEntry(logger), + } +} + +// Subscribe subscribes a channel to a PolochonEventers +// It fetches or creates a new Eventer for the needed polochon, call Launch() +// in a go routine and then Append the user to the list of subscribed users +func (p *PolochonEventers) Subscribe(chanl *Channel) error { + p.Lock() + defer p.Unlock() + + p.log.Debugf("subscribing with the user %s", chanl.User.Name) + if !chanl.User.PolochonActivated { + return fmt.Errorf("polochon not activated") + } + + // If we have the polochon, just append the user to the list of users + // listening + tn, ok := p.polochons[chanl.User.PolochonID.String] + if !ok { + p.log.Debugf("Eventer not already created, create it for polochon %s", chanl.User.PolochonID.String) + + // Get the user's polochon + polo, err := chanl.User.GetPolochon(chanl.db) + if err != nil { + return fmt.Errorf("failed to retrieve polochon") + } + + // Create a new Eventer for this polochon + tn, err = p.NewEventer(polo, p.log) + if err != nil { + return err + } + go func() { + err := tn.Launch() + if err != nil { + delete(p.polochons, chanl.User.PolochonID.String) + } + }() + + p.polochons[chanl.User.PolochonID.String] = tn + } + + // Add the Channel to the Eventer + tn.Append(chanl) + + p.log.Debugf("Eventer created for polochon %s ...", chanl.User.PolochonID.String) + return nil +} + +// Unsubscribe unsubscribes a client from receiving any more events from +// the Eventer +// If the list of users is empty, delete the Eventer +func (p *PolochonEventers) Unsubscribe(chanl *Channel) { + p.Lock() + defer p.Unlock() + + p.log.Debugf("unsubscribing from %s with %s", p.Name, chanl.User.Name) + + tn, ok := p.polochons[chanl.User.PolochonID.String] + if !ok { + p.log.Warnf("no Eventer for polochon %s, not unsubscribing", chanl.User.PolochonID.String) + return + } + + tn.Unsubscribe(chanl) + + if len(tn.Subscribers()) == 0 { + p.log.Debugf("just deleted the last user of this polochon instance, delete it") + tn.Finish() + // Delete the polochon from the Eventer when it's finished + delete(p.polochons, chanl.User.PolochonID.String) + } +} + +// Unsubscribe unsubscribes a client from receiving any more events from +// the Eventer +func (e *BaseEventer) Unsubscribe(chanl *Channel) error { + // Need to delete the given channel from the channel list + // TODO: Find a better way to handle this + // It is needed when the user changes his polochon, or get deactivated + // If the ID of the channel is empty, delete all the channels of + // this user + l := len(e.users) + i := 0 + for i < l { + // Ugly ... + // If we don't have a channel ID, check the user ID + // Else check for the channel ID + if (chanl.id == "" && e.users[i].User.ID != chanl.User.ID) || + (chanl.id != "" && e.users[i].id != chanl.id) { + i++ + continue + } + e.log.Debugf("found the user channel %s for user %s, deleting it...", chanl.id, chanl.User.Name) + + // Delete this event from the list of events the channel is subscribed + delete(e.users[i].events, e.name) + + // Send the disconnected event + event := ServerEvent{ + Event: Event{ + Type: e.name, + Status: Disconnected, + }, + } + + e.users[i].serverEventStream <- event + + // Replace the current element with the last one + e.users[i] = e.users[len(e.users)-1] + e.users = e.users[:len(e.users)-1] + l-- + } + return nil +} + +// FatalError sends an error into the errorStream channel and tell all the +// clients that the error is fatal, the notifier won't communicate until a new +// subscribe +func (e *BaseEventer) FatalError(err error) { + for _, chanl := range e.users { + // Send the error + chanl.FatalError(e.name, err) + // Delete the event from the channel events + delete(chanl.events, e.name) + } +} + +// Append just append a channel to the list of users +func (e *BaseEventer) Append(chanl *Channel) { + e.users = append(e.users, chanl) +} + +// Subscribers just returns the list of subscribers +func (e *BaseEventer) Subscribers() []*Channel { + return e.users +} + +// NotifyAll notifies all the listener +func (e *BaseEventer) NotifyAll(data interface{}) { + // If they're different, prepare the event + event := ServerEvent{ + Event: Event{ + Type: e.name, + Status: OK, + }, + Data: data, + } + + // Send the events to all the subscribed users + for _, chanl := range e.users { + e.log.Debugf("sending event to %s", chanl.User.Name) + chanl.serverEventStream <- event + } +} diff --git a/backend/events/torrent_eventer.go b/backend/events/torrent_eventer.go new file mode 100644 index 0000000..d6317b9 --- /dev/null +++ b/backend/events/torrent_eventer.go @@ -0,0 +1,128 @@ +package events + +import ( + "fmt" + "reflect" + "time" + + "git.quimbo.fr/odwrtw/canape/backend/models" + "github.com/odwrtw/papi" + "github.com/sirupsen/logrus" +) + +// TorrentEventer represents the Eventer for torrents +type TorrentEventer struct { + *BaseEventer + done chan struct{} + pClient *papi.Client + torrents []papi.Torrent +} + +var torrentEventName = "torrents" + +// NewTorrentEventers returns a new PolochonEventers for torrents +func NewTorrentEventers() *PolochonEventers { + eventer := NewEventers() + eventer.NewEventer = NewTorrentEventer + eventer.Name = torrentEventName + return eventer +} + +// NewTorrentEventer returns a new Eventer for a specific Polochon +func NewTorrentEventer(polo *models.Polochon, log *logrus.Entry) (Eventer, error) { + // Create a new papi client + client, err := polo.NewPapiClient() + if err != nil { + log.Warnf("failed to create the polochon client %s", err) + return nil, fmt.Errorf("failed to instanciate polochon client") + } + + // This is the first time this polochon is requested, create the TorrentEventer + tn := &TorrentEventer{ + BaseEventer: &BaseEventer{ + users: []*Channel{}, + log: log, + name: torrentEventName, + }, + pClient: client, + done: make(chan struct{}), + torrents: []papi.Torrent{}, + } + + return tn, nil +} + +// Append implements the Eventer interface +// It just appends a channel to the list of users +// When the user is appended, send him the torrents infos we have +func (t *TorrentEventer) Append(chanl *Channel) { + event := ServerEvent{ + Event: Event{ + Type: torrentEventName, + Status: OK, + }, + Data: t.torrents, + } + + chanl.serverEventStream <- event + + t.BaseEventer.Append(chanl) +} + +// Launch implements the Eventer interface +// It starts a ticker to fetch torrents and notify the users +func (t *TorrentEventer) Launch() error { + // Create the timer that will check for the torrents every X seconds + timeTicker := time.NewTicker(10 * time.Second) + defer timeTicker.Stop() + + err := t.torrentsUpdate() + if err != nil { + t.log.Warnf("got error getting torrent update: %s", err) + t.FatalError(err) + return err + } + + for { + select { + case <-timeTicker.C: + err := t.torrentsUpdate() + if err != nil { + t.log.Warnf("got error getting torrent update: %s", err) + t.FatalError(err) + return err + } + case <-t.done: + t.log.Debug("quit torrent notifier") + return nil + } + } +} + +// torrentsUpdate sends to the eventStream if torrents change +func (t *TorrentEventer) torrentsUpdate() error { + // Get torrents + torrents, err := t.pClient.GetTorrents() + if err != nil { + return err + } + + if reflect.DeepEqual(t.torrents, torrents) { + return nil + } + + t.log.Debugf("torrents have changed!") + + t.NotifyAll(torrents) + + t.torrents = torrents + + return nil +} + +// Finish implements the Eventer interface +// It is called when there is no more users subscribed +func (t *TorrentEventer) Finish() { + t.log.Debugf("sending the done channel") + t.done <- struct{}{} +} diff --git a/backend/events/torrents.go b/backend/events/torrents.go deleted file mode 100644 index c840ed2..0000000 --- a/backend/events/torrents.go +++ /dev/null @@ -1,92 +0,0 @@ -package events - -import ( - "reflect" - "time" - - "git.quimbo.fr/odwrtw/canape/backend/models" - "github.com/odwrtw/papi" - "github.com/sirupsen/logrus" -) - -// TorrentNotifier is a struct implementing the Notifier interface -type TorrentNotifier struct { - *Notifier - user *models.User - client *papi.Client - torrents []papi.Torrent - log *logrus.Entry -} - -// Name implements the Notifier interface -func (t *TorrentNotifier) Name() string { - return "torrents" -} - -// Launch implements the Notifier interface -func (t *TorrentNotifier) Launch() { - // Create the timer that will check for the torrents every X seconds - timeTicker := time.NewTicker(10 * time.Second) - defer timeTicker.Stop() - - // Create a new papi client - client, err := t.user.NewPapiClient() - if err != nil { - return - } - t.client = client - - err = t.torrentsUpdate() - if err != nil { - t.log.Warnf("got error getting torrent update: %s", err) - t.FatalError(err) - return - } - - for { - select { - case <-timeTicker.C: - err := t.torrentsUpdate() - if err != nil { - t.log.Warnf("got error getting torrent update: %s", err) - t.FatalError(err) - return - } - case <-t.done: - t.log.Info("quit torrent notifier") - return - } - } -} - -// FatalError is a wrapper around Notifier FatalError -func (t *TorrentNotifier) FatalError(err error) { - t.Notifier.FatalError(t.Name(), err) -} - -// torrentsUpdate sends to the eventStream if torrents change -func (t *TorrentNotifier) torrentsUpdate() error { - // Get torrents - torrents, err := t.client.GetTorrents() - if err != nil { - return err - } - - if reflect.DeepEqual(t.torrents, torrents) { - return nil - } - - // If they're different, send the event - event := ServerEvent{ - Event: Event{ - Type: t.Name(), - Status: OK, - }, - Data: torrents, - } - t.eventStream <- event - - t.torrents = torrents - - return nil -} diff --git a/backend/events/video_eventer.go b/backend/events/video_eventer.go new file mode 100644 index 0000000..5f00ca6 --- /dev/null +++ b/backend/events/video_eventer.go @@ -0,0 +1,41 @@ +package events + +import ( + "git.quimbo.fr/odwrtw/canape/backend/models" + "github.com/sirupsen/logrus" +) + +// VideoEventer represents the Eventer for tests +type VideoEventer struct { + *BaseEventer +} + +var videoEventName = "newVideo" + +// NewVideoEventers implements the Eventer interface +func NewVideoEventers() *PolochonEventers { + eventer := NewEventers() + eventer.NewEventer = NewVideoEventer + eventer.Name = videoEventName + return eventer +} + +// NewVideoEventer returns a new Eventer +func NewVideoEventer(polo *models.Polochon, log *logrus.Entry) (Eventer, error) { + return &VideoEventer{ + BaseEventer: &BaseEventer{ + users: []*Channel{}, + log: log, + name: videoEventName, + }, + }, nil +} + +// Launch implements the Eventer interface +func (e *VideoEventer) Launch() error { + return nil +} + +// Finish implements the Eventer interface +func (e *VideoEventer) Finish() { +} diff --git a/backend/external_medias/handlers.go b/backend/external_medias/handlers.go index 84710bf..26ec0c7 100644 --- a/backend/external_medias/handlers.go +++ b/backend/external_medias/handlers.go @@ -59,7 +59,7 @@ func GetMovies(env *web.Env, user *models.User, source string, category string) } // Create a papi client - client, err := user.NewPapiClient() + client, err := user.NewPapiClient(env.Database) if err != nil { return nil, err } @@ -119,7 +119,7 @@ func GetShows(env *web.Env, user *models.User, source string, category string, f } // Create a papi client - client, err := user.NewPapiClient() + client, err := user.NewPapiClient(env.Database) if err != nil { return nil, err } diff --git a/backend/main.go b/backend/main.go index 2884c11..281d8f8 100644 --- a/backend/main.go +++ b/backend/main.go @@ -6,9 +6,9 @@ import ( "os" "git.quimbo.fr/odwrtw/canape/backend/auth" - "git.quimbo.fr/odwrtw/canape/backend/models" "git.quimbo.fr/odwrtw/canape/backend/config" extmedias "git.quimbo.fr/odwrtw/canape/backend/external_medias" + "git.quimbo.fr/odwrtw/canape/backend/models" "git.quimbo.fr/odwrtw/canape/backend/ratings" "git.quimbo.fr/odwrtw/canape/backend/web" @@ -97,6 +97,8 @@ func main() { n := negroni.Classic() // Middleware for setting ips n.Use(auth.NewIPMiddleware(log)) + // Middleware for request id + n.Use(auth.NewRequestIDMiddleware(log)) // Middleware for authentication n.Use(authMiddleware) // Serve static files diff --git a/backend/models/polochons.go b/backend/models/polochons.go index 14702d9..54e1235 100644 --- a/backend/models/polochons.go +++ b/backend/models/polochons.go @@ -2,10 +2,12 @@ package models import ( "database/sql" + "errors" "fmt" "git.quimbo.fr/odwrtw/canape/backend/sqly" "github.com/jmoiron/sqlx" + "github.com/odwrtw/papi" ) const ( @@ -15,7 +17,7 @@ const ( RETURNING id;` getPolochonQuery = `SELECT * FROM polochons WHERE name=$1;` getPolochonByIDQuery = `SELECT * FROM polochons WHERE id=$1;` - getPolochonsByUserIDQuery = `SELECT * FROM polochons WHERE admin_id=$1;` + getPolochonsByUserIDQuery = `SELECT * FROM polochons WHERE admin_id=$1 ORDER BY created_at DESC;` updatePolochonQuery = `UPDATE polochons SET name=:name, url=:url, token=:token, auth_token=:auth_token, admin_id=:admin_id @@ -23,10 +25,10 @@ const ( RETURNING *;` deletePolochonQuery = `DELETE FROM polochons WHERE id=:id;` - getAllPolochonsQuery = `SELECT * FROM polochons order by created_at;` + getAllPolochonsQuery = `SELECT * FROM polochons ORDER BY created_at DESC;` ) -// ErrUnknownPolochon returned when a polochon does'nt exist +// ErrUnknownPolochon returned when a polochon doesn't exist var ErrUnknownPolochon = fmt.Errorf("polochons: polochon does'nt exist") // Polochon represents a polochon @@ -41,20 +43,7 @@ type Polochon struct { Users []*User `json:"users"` } -// Get returns polochon with specified name -func GetPolochon(q sqlx.Queryer, name string) (*Polochon, error) { - p := &Polochon{} - err := q.QueryRowx(getPolochonQuery, name).StructScan(p) - if err != nil { - if err == sql.ErrNoRows { - return nil, ErrUnknownPolochon - } - return nil, err - } - return p, nil -} - -// GetByID returns polochon using its id +// GetPolochonByID returns polochon using its id func GetPolochonByID(q sqlx.Queryer, id string) (*Polochon, error) { p := &Polochon{} err := q.QueryRowx(getPolochonByIDQuery, id).StructScan(p) @@ -67,7 +56,7 @@ func GetPolochonByID(q sqlx.Queryer, id string) (*Polochon, error) { return p, nil } -// GetAll returns all the polochons +// GetAllPolochons returns all the polochons func GetAllPolochons(db *sqlx.DB) ([]*Polochon, error) { polochons := []*Polochon{} err := db.Select(&polochons, getAllPolochonsQuery) @@ -77,7 +66,7 @@ func GetAllPolochons(db *sqlx.DB) ([]*Polochon, error) { return polochons, nil } -// GetAllByUser returns all the polochons owned by the user +// GetAllPolochonsByUser returns all the polochons owned by the user func GetAllPolochonsByUser(db *sqlx.DB, id string) ([]*Polochon, error) { polochons := []*Polochon{} err := db.Select(&polochons, getPolochonsByUserIDQuery, id) @@ -118,3 +107,16 @@ func (p *Polochon) Delete(ex *sqlx.DB) error { } return nil } + +// NewPapiClient creates a new papi client for the given polochon +func (p *Polochon) NewPapiClient() (*papi.Client, error) { + client, err := papi.New(p.URL) + if err != nil { + return nil, errors.New("error getting papi client") + } + + if p.Token != "" { + client.SetToken(p.Token) + } + return client, nil +} diff --git a/backend/models/users.go b/backend/models/users.go index 998ae92..1469deb 100644 --- a/backend/models/users.go +++ b/backend/models/users.go @@ -2,11 +2,9 @@ package models import ( "database/sql" - "encoding/json" "errors" "fmt" - "git.quimbo.fr/odwrtw/canape/backend/config" "git.quimbo.fr/odwrtw/canape/backend/sqly" "github.com/jmoiron/sqlx" "github.com/jmoiron/sqlx/types" @@ -53,73 +51,23 @@ type User struct { Activated bool `json:"activated"` PolochonID sql.NullString `json:"polochon_id" db:"polochon_id"` PolochonActivated bool `json:"polochon_activated" db:"polochon_activated"` -} - -// GetConfig unmarshal json from specified config key into v -func (u *User) GetConfig(key string, v interface{}) error { - var configMap map[string]*json.RawMessage - err := u.RawConfig.Unmarshal(&configMap) - if err != nil { - return err - } - if raw, ok := configMap[key]; ok { - return json.Unmarshal(*raw, v) - } - return nil -} - -// SetConfig marshal v into json for specified config key -func (u *User) SetConfig(key string, v interface{}) error { - var configMap map[string]*json.RawMessage - if err := u.RawConfig.Unmarshal(&configMap); err != nil { - return err - } - - b, err := json.Marshal(v) - if err != nil { - return err - } - - if configMap == nil { - configMap = map[string]*json.RawMessage{} - } - - r := json.RawMessage(b) - configMap[key] = &r - b, err = json.Marshal(configMap) - if err != nil { - return err - } - - return u.RawConfig.UnmarshalJSON(b) -} - -// NewConfig creates a new empty config -func (u *User) NewConfig() error { - configMap := make(map[string]*json.RawMessage) - b, err := json.Marshal(configMap) - if err != nil { - return err - } - - return u.RawConfig.UnmarshalJSON(b) + Polochon *Polochon `json:"polochon"` } // NewPapiClient creates a new papi client for the given user -func (u *User) NewPapiClient() (*papi.Client, error) { - var polochonConfig config.UserPolochon - err := u.GetConfig("polochon", &polochonConfig) +func (u *User) NewPapiClient(db *sqlx.DB) (*papi.Client, error) { + polochon, err := u.GetPolochon(db) if err != nil { - return nil, errors.New("missing polochon config") + return nil, err } - client, err := papi.New(polochonConfig.URL) + client, err := papi.New(polochon.URL) if err != nil { return nil, errors.New("error getting papi client") } - if polochonConfig.Token != "" { - client.SetToken(polochonConfig.Token) + if u.Token != "" { + client.SetToken(u.Token) } return client, nil } @@ -219,3 +167,8 @@ func (u *User) IsAdmin() bool { func (u *User) IsActivated() bool { return u.Activated } + +// GetPolochon returns the user's polochon +func (u *User) GetPolochon(db *sqlx.DB) (*Polochon, error) { + return GetPolochonByID(db, u.PolochonID.String) +} diff --git a/backend/movies/handlers.go b/backend/movies/handlers.go index a560ac2..b04f5db 100644 --- a/backend/movies/handlers.go +++ b/backend/movies/handlers.go @@ -6,7 +6,6 @@ import ( "net/http" "git.quimbo.fr/odwrtw/canape/backend/auth" - "git.quimbo.fr/odwrtw/canape/backend/config" "git.quimbo.fr/odwrtw/canape/backend/models" "git.quimbo.fr/odwrtw/canape/backend/subtitles" "git.quimbo.fr/odwrtw/canape/backend/web" @@ -27,13 +26,6 @@ func PolochonMoviesHandler(env *web.Env, w http.ResponseWriter, r *http.Request) return env.RenderError(w, err) } - // Create a new polochon Detailer to get infos about the movies we just got - var polochonConfig config.UserPolochon - err = user.GetConfig("polochon", &polochonConfig) - if err != nil { - return err - } - // Get details with the polochon Detailer for each polochon.Movies we have for _, m := range movies { // First try from the db @@ -66,7 +58,7 @@ func RefreshMovieHandler(env *web.Env, w http.ResponseWriter, r *http.Request) e user := auth.GetCurrentUser(r, env.Log) // Create a new papi client - client, err := user.NewPapiClient() + client, err := user.NewPapiClient(env.Database) if err != nil { return env.RenderError(w, err) } @@ -107,7 +99,7 @@ func SearchMovie(env *web.Env, w http.ResponseWriter, r *http.Request) error { user := auth.GetCurrentUser(r, env.Log) // Create a new papi client - client, err := user.NewPapiClient() + client, err := user.NewPapiClient(env.Database) if err != nil { return env.RenderError(w, err) } @@ -189,7 +181,7 @@ func PolochonDeleteHandler(env *web.Env, w http.ResponseWriter, r *http.Request) user := auth.GetCurrentUser(r, env.Log) // Create a new papi client - client, err := user.NewPapiClient() + client, err := user.NewPapiClient(env.Database) if err != nil { return env.RenderError(w, err) } @@ -231,7 +223,7 @@ func GetWishlistHandler(env *web.Env, w http.ResponseWriter, r *http.Request) er user := auth.GetCurrentUser(r, env.Log) // Create a new papi client - client, err := user.NewPapiClient() + client, err := user.NewPapiClient(env.Database) if err != nil { return env.RenderError(w, err) } @@ -293,7 +285,7 @@ func RefreshMovieSubtitlesHandler(env *web.Env, w http.ResponseWriter, r *http.R user := auth.GetCurrentUser(r, env.Log) // Create a new papi client - client, err := user.NewPapiClient() + client, err := user.NewPapiClient(env.Database) if err != nil { return env.RenderError(w, err) } @@ -327,7 +319,7 @@ func DownloadVVTSubtitle(env *web.Env, w http.ResponseWriter, r *http.Request) e user := auth.GetCurrentUser(r, env.Log) // Create a new papi client - client, err := user.NewPapiClient() + client, err := user.NewPapiClient(env.Database) if err != nil { return env.RenderError(w, err) } diff --git a/backend/movies/movies.go b/backend/movies/movies.go index 23f9898..03038ea 100644 --- a/backend/movies/movies.go +++ b/backend/movies/movies.go @@ -254,7 +254,7 @@ func getPolochonMovies(user *models.User, env *web.Env) ([]*Movie, error) { movies := []*Movie{} // Create a papi client - client, err := user.NewPapiClient() + client, err := user.NewPapiClient(env.Database) if err != nil { return movies, err } diff --git a/backend/polochons/handlers.go b/backend/polochons/handlers.go index 2a1d594..7537726 100644 --- a/backend/polochons/handlers.go +++ b/backend/polochons/handlers.go @@ -164,28 +164,20 @@ func EditPolochonHandler(env *web.Env, w http.ResponseWriter, r *http.Request) e return env.RenderOK(w, "Polochon updated") } -// PolochonDeactivateUserHandler handles the users of a polochon -func PolochonDeactivateUserHandler(env *web.Env, w http.ResponseWriter, r *http.Request) error { - if err := PolochonActivateUser(env, w, r, false); err != nil { - return env.RenderError(w, err) - } - return env.RenderOK(w, "User deactivated") -} - -// PolochonActivateUserHandler handles the users of a polochon -func PolochonActivateUserHandler(env *web.Env, w http.ResponseWriter, r *http.Request) error { - if err := PolochonActivateUser(env, w, r, true); err != nil { - return env.RenderError(w, err) - } - return env.RenderOK(w, "User activated") -} - -// PolochonActivateUser activates or deactivate a user's polochon -func PolochonActivateUser(env *web.Env, w http.ResponseWriter, r *http.Request, activated bool) error { +// PolochonUserHandler edit a user's polochon config +func PolochonUserHandler(env *web.Env, w http.ResponseWriter, r *http.Request) error { log := env.Log.WithFields(logrus.Fields{ "function": "polochons.PolochonUserHandler", }) - log.Debugf("editing polochon users ...") + log.Debugf("editing polochon user") + + var data struct { + Activated bool `json:"activated"` + Token string `json:"token"` + } + if err := json.NewDecoder(r.Body).Decode(&data); err != nil { + return err + } user := auth.GetCurrentUser(r, env.Log) @@ -209,7 +201,43 @@ func PolochonActivateUser(env *web.Env, w http.ResponseWriter, r *http.Request, return env.RenderError(w, err) } - u.PolochonActivated = activated + u.PolochonActivated = data.Activated + u.Token = data.Token - return u.Update(env.Database) + err = u.Update(env.Database) + if err != nil { + return env.RenderError(w, err) + } + + return env.RenderOK(w, "Polochon user updated") +} + +// DeletePolochonHandler deletes a polochon +func DeletePolochonHandler(env *web.Env, w http.ResponseWriter, r *http.Request) error { + log := env.Log.WithFields(logrus.Fields{ + "function": "polochons.DeletePolochonHandler", + }) + log.Debugf("deleting polochon user") + + user := auth.GetCurrentUser(r, env.Log) + + // Get the polochon + vars := mux.Vars(r) + id := vars["id"] + p, err := models.GetPolochonByID(env.Database, id) + if err != nil { + return env.RenderError(w, err) + } + + // Check that the logged-in user is the polochon admin + if p.AdminID != user.ID { + return env.RenderError(w, fmt.Errorf("forbidden")) + } + + err = p.Delete(env.Database) + if err != nil { + return env.RenderError(w, err) + } + + return env.RenderOK(w, "Polochon deleted") } diff --git a/backend/routes.go b/backend/routes.go index ffae79c..10fe5e9 100644 --- a/backend/routes.go +++ b/backend/routes.go @@ -2,9 +2,9 @@ package main import ( admin "git.quimbo.fr/odwrtw/canape/backend/admins" - "git.quimbo.fr/odwrtw/canape/backend/models" "git.quimbo.fr/odwrtw/canape/backend/events" extmedias "git.quimbo.fr/odwrtw/canape/backend/external_medias" + "git.quimbo.fr/odwrtw/canape/backend/models" "git.quimbo.fr/odwrtw/canape/backend/movies" "git.quimbo.fr/odwrtw/canape/backend/polochons" "git.quimbo.fr/odwrtw/canape/backend/ratings" @@ -27,11 +27,11 @@ func setupRoutes(env *web.Env) { env.Handle("/users/polochons", polochons.GetPolochonsHandler).WithRole(models.UserRole).Methods("GET") // Polochon's route - env.Handle("/polochons", polochons.GetPublicPolochonsHandler).Methods("GET") + env.Handle("/polochons", polochons.GetPublicPolochonsHandler).WithRole(models.UserRole).Methods("GET") env.Handle("/polochons", polochons.NewPolochonHandler).WithRole(models.UserRole).Methods("POST") env.Handle("/polochons/{id}", polochons.EditPolochonHandler).WithRole(models.UserRole).Methods("POST") - env.Handle("/polochons/{id}/users/{user_id}", polochons.PolochonActivateUserHandler).WithRole(models.UserRole).Methods("POST") - env.Handle("/polochons/{id}/users/{user_id}", polochons.PolochonDeactivateUserHandler).WithRole(models.UserRole).Methods("DELETE") + env.Handle("/polochons/{id}", polochons.DeletePolochonHandler).WithRole(models.UserRole).Methods("DELETE") + env.Handle("/polochons/{id}/users/{user_id}", polochons.PolochonUserHandler).WithRole(models.UserRole).Methods("POST") // Movies routes env.Handle("/movies/polochon", movies.PolochonMoviesHandler).WithRole(models.UserRole).Methods("GET") @@ -80,6 +80,9 @@ func setupRoutes(env *web.Env) { // Route to handle websocket events env.Handle("/events", events.WsHandler).WithRole(models.UserRole).Methods("GET") + // Route to handle websocket events + env.Handle("/events/polochons/{id}/hook", events.PolochonHookHandler).Methods("POST") + // Admin routes env.Handle("/admins/users", admin.GetUsersHandler).WithRole(models.AdminRole).Methods("GET") env.Handle("/admins/users", admin.UpdateUserHandler).WithRole(models.AdminRole).Methods("POST") diff --git a/backend/shows/handlers.go b/backend/shows/handlers.go index 9e6e81e..2e824a9 100644 --- a/backend/shows/handlers.go +++ b/backend/shows/handlers.go @@ -28,7 +28,7 @@ func GetDetailsHandler(env *web.Env, w http.ResponseWriter, r *http.Request) err user := auth.GetCurrentUser(r, env.Log) - client, err := user.NewPapiClient() + client, err := user.NewPapiClient(env.Database) if err != nil { return env.RenderError(w, err) } @@ -74,7 +74,7 @@ func RefreshShowHandler(env *web.Env, w http.ResponseWriter, r *http.Request) er user := auth.GetCurrentUser(r, env.Log) - client, err := user.NewPapiClient() + client, err := user.NewPapiClient(env.Database) if err != nil { return env.RenderError(w, err) } @@ -134,7 +134,7 @@ func SearchShow(env *web.Env, w http.ResponseWriter, r *http.Request) error { env.Log.Debugf("got %d shows doing search %q", len(shows), search) - client, err := user.NewPapiClient() + client, err := user.NewPapiClient(env.Database) if err != nil { return env.RenderError(w, err) } @@ -216,7 +216,7 @@ func DeleteFromWishlist(env *web.Env, w http.ResponseWriter, r *http.Request) er func GetWishlistHandler(env *web.Env, w http.ResponseWriter, r *http.Request) error { user := auth.GetCurrentUser(r, env.Log) - client, err := user.NewPapiClient() + client, err := user.NewPapiClient(env.Database) if err != nil { return env.RenderError(w, err) } @@ -290,7 +290,7 @@ func RefreshEpisodeHandler(env *web.Env, w http.ResponseWriter, r *http.Request) user := auth.GetCurrentUser(r, env.Log) - client, err := user.NewPapiClient() + client, err := user.NewPapiClient(env.Database) if err != nil { return env.RenderError(w, err) } @@ -337,7 +337,7 @@ func RefreshEpisodeSubtitlesHandler(env *web.Env, w http.ResponseWriter, r *http user := auth.GetCurrentUser(r, env.Log) // Create a new papi client - client, err := user.NewPapiClient() + client, err := user.NewPapiClient(env.Database) if err != nil { return env.RenderError(w, err) } @@ -378,7 +378,7 @@ func DownloadVVTSubtitle(env *web.Env, w http.ResponseWriter, r *http.Request) e user := auth.GetCurrentUser(r, env.Log) // Create a new papi client - client, err := user.NewPapiClient() + client, err := user.NewPapiClient(env.Database) if err != nil { return env.RenderError(w, err) } diff --git a/backend/shows/shows.go b/backend/shows/shows.go index ed59f68..c008557 100644 --- a/backend/shows/shows.go +++ b/backend/shows/shows.go @@ -200,7 +200,7 @@ func (s *Show) imgFile(imgType string) string { func getPolochonShows(env *web.Env, user *models.User) ([]*Show, error) { shows := []*Show{} - client, err := user.NewPapiClient() + client, err := user.NewPapiClient(env.Database) if err != nil { return shows, err } diff --git a/backend/torrents/handlers.go b/backend/torrents/handlers.go index 3427c33..9b34787 100644 --- a/backend/torrents/handlers.go +++ b/backend/torrents/handlers.go @@ -29,7 +29,7 @@ func DownloadHandler(env *web.Env, w http.ResponseWriter, r *http.Request) error user := auth.GetCurrentUser(r, env.Log) - client, err := user.NewPapiClient() + client, err := user.NewPapiClient(env.Database) if err != nil { return env.RenderError(w, err) } @@ -46,7 +46,7 @@ func DownloadHandler(env *web.Env, w http.ResponseWriter, r *http.Request) error func ListHandler(env *web.Env, w http.ResponseWriter, r *http.Request) error { user := auth.GetCurrentUser(r, env.Log) - client, err := user.NewPapiClient() + client, err := user.NewPapiClient(env.Database) if err != nil { return env.RenderError(w, err) } @@ -67,7 +67,7 @@ func RemoveHandler(env *web.Env, w http.ResponseWriter, r *http.Request) error { vars := mux.Vars(r) torrentID := vars["id"] - client, err := user.NewPapiClient() + client, err := user.NewPapiClient(env.Database) if err != nil { return env.RenderError(w, err) } diff --git a/backend/users/handlers.go b/backend/users/handlers.go index 90f3ce6..175d57c 100644 --- a/backend/users/handlers.go +++ b/backend/users/handlers.go @@ -8,6 +8,7 @@ import ( "git.quimbo.fr/odwrtw/canape/backend/auth" "git.quimbo.fr/odwrtw/canape/backend/config" + "git.quimbo.fr/odwrtw/canape/backend/events" "git.quimbo.fr/odwrtw/canape/backend/models" "git.quimbo.fr/odwrtw/canape/backend/tokens" "git.quimbo.fr/odwrtw/canape/backend/web" @@ -46,11 +47,6 @@ func SignupPOSTHandler(e *web.Env, w http.ResponseWriter, r *http.Request) error return err } - err = user.NewConfig() - if err != nil { - return err - } - if err = user.Add(e.Database); err != nil { return err } @@ -116,7 +112,6 @@ func EditHandler(e *web.Env, w http.ResponseWriter, r *http.Request) error { var data struct { PolochonID string `json:"polochon_id"` - PolochonToken string `json:"polochon_token"` Password string `json:"password"` PasswordConfirm string `json:"password_confirm"` } @@ -138,19 +133,24 @@ func EditHandler(e *web.Env, w http.ResponseWriter, r *http.Request) error { } } + // If we changed polochon, we need to unsubscribe it from previous events if data.PolochonID != "" && user.PolochonID.String != data.PolochonID { + e.Log.Info("unsubscribing user...") _, err := models.GetPolochonByID(e.Database, data.PolochonID) if err != nil { return e.RenderError(w, fmt.Errorf("Could not find such polochon")) } + + // Need to unsubscribe the user from all the eventers + events.Unsubscribe(user) + user.PolochonID = sql.NullString{ String: data.PolochonID, Valid: true, } user.PolochonActivated = false - } - user.Token = data.PolochonToken + } // Save the user with the new configurations if err := user.Update(e.Database); err != nil { @@ -219,7 +219,7 @@ func GetModulesStatus(e *web.Env, w http.ResponseWriter, r *http.Request) error user := auth.GetCurrentUser(r, e.Log) // Create a papi client - client, err := user.NewPapiClient() + client, err := user.NewPapiClient(e.Database) if err != nil { return e.RenderError(w, fmt.Errorf("error while getting user")) } diff --git a/go.mod b/go.mod index 6c6f943..a4d1a7f 100644 --- a/go.mod +++ b/go.mod @@ -5,12 +5,15 @@ go 1.12 require ( github.com/dgrijalva/jwt-go v3.2.0+incompatible github.com/go-sql-driver/mysql v1.4.1 // indirect + github.com/gofrs/uuid v3.2.0+incompatible github.com/gorilla/mux v1.7.1 github.com/gorilla/websocket v1.4.0 github.com/gregdel/srt2vtt v0.0.0-20170314031115-46562d19ab2d github.com/jmoiron/sqlx v1.2.0 + github.com/kr/pretty v0.1.0 github.com/lib/pq v1.1.1 github.com/mattn/go-sqlite3 v1.10.0 // indirect + github.com/meatballhat/negroni-logrus v0.0.0-20170801195057-31067281800f github.com/nfnt/resize v0.0.0-20180221191011-83c6a9932646 github.com/odwrtw/papi v0.0.0-20190511132159-936937ad8b6a github.com/odwrtw/polochon v0.0.0-20190510120140-0141d4124793 diff --git a/go.sum b/go.sum index 5e3849b..8467f9e 100644 --- a/go.sum +++ b/go.sum @@ -17,6 +17,8 @@ github.com/go-sql-driver/mysql v1.4.0 h1:7LxgVwFb2hIQtMm87NdgAVfXjnt4OePseqT1tKx github.com/go-sql-driver/mysql v1.4.0/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG5ZlKdlhCg5w= github.com/go-sql-driver/mysql v1.4.1 h1:g24URVg0OFbNUTx9qqY1IRZ9D9z3iPyi5zKhQZpNwpA= github.com/go-sql-driver/mysql v1.4.1/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG5ZlKdlhCg5w= +github.com/gofrs/uuid v3.2.0+incompatible h1:y12jRkkFxsd7GpqdSZ+/KCs/fJbqpEXSGd4+jfEaewE= +github.com/gofrs/uuid v3.2.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM= github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/gorilla/mux v1.7.1 h1:Dw4jY2nghMMRsh1ol8dv1axHkDwMQK2DHerMNJsIpJU= github.com/gorilla/mux v1.7.1/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs= @@ -53,6 +55,7 @@ github.com/mattn/go-sqlite3 v1.9.0 h1:pDRiWfl+++eC2FEFRy6jXmQlvp4Yh3z1MJKg4UeYM/ github.com/mattn/go-sqlite3 v1.9.0/go.mod h1:FPy6KqzDD04eiIsT53CuJW3U88zkxoIYsOqkbpncsNc= github.com/mattn/go-sqlite3 v1.10.0 h1:jbhqpg7tQe4SupckyijYiy0mJJ/pRyHvXf7JdWK860o= github.com/mattn/go-sqlite3 v1.10.0/go.mod h1:FPy6KqzDD04eiIsT53CuJW3U88zkxoIYsOqkbpncsNc= +github.com/meatballhat/negroni-logrus v0.0.0-20170801195057-31067281800f h1:V6GHkMOIsnpGDasS1iYiNxEYTY8TmyjQXEF8PqYkKQ8= github.com/meatballhat/negroni-logrus v0.0.0-20170801195057-31067281800f/go.mod h1:Ylx55XGW4gjY7McWT0pgqU0aQquIOChDnYkOVbSuF/c= github.com/mitchellh/mapstructure v1.1.2 h1:fmNYVwqnSfB9mZU6OS2O6GsXM+wcskZDuKQzvN1EDeE= github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=