Add the Eventers logic and handlers

This commit is contained in:
Lucas BEE 2019-06-21 09:04:48 +00:00
parent 07eefe1870
commit 3b397300bc
23 changed files with 697 additions and 361 deletions

View File

@ -1,11 +1,12 @@
package admin package admin
import ( import (
"database/sql"
"encoding/json" "encoding/json"
"fmt" "fmt"
"net/http" "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/models"
"git.quimbo.fr/odwrtw/canape/backend/web" "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") log.Debug("Getting users")
// Get all users
users, err := models.GetAllUsers(env.Database) users, err := models.GetAllUsers(env.Database)
if err != nil { if err != nil {
return env.RenderError(w, err) 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) return env.RenderJSON(w, users)
} }
@ -64,12 +86,13 @@ func UpdateUserHandler(env *web.Env, w http.ResponseWriter, r *http.Request) err
}) })
var data struct { var data struct {
ID string `json:"userId"` ID string `json:"userId"`
Admin bool `json:"admin"` Admin bool `json:"admin"`
Activated bool `json:"activated"` Activated bool `json:"activated"`
PolochonURL string `json:"polochonUrl"` PolochonID string `json:"polochonId"`
PolochonToken string `json:"polochonToken"` PolochonToken string `json:"polochonToken"`
Password string `json:"password"` PolochonActivated bool `json:"polochonActivated"`
Password string `json:"password"`
} }
if err := json.NewDecoder(r.Body).Decode(&data); err != nil { if err := json.NewDecoder(r.Body).Decode(&data); err != nil {
return err return err
@ -84,17 +107,14 @@ func UpdateUserHandler(env *web.Env, w http.ResponseWriter, r *http.Request) err
return 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.Admin = data.Admin
user.Activated = data.Activated user.Activated = data.Activated
user.PolochonActivated = data.PolochonActivated
user.PolochonID = sql.NullString{
String: data.PolochonID,
Valid: true,
}
user.Token = data.PolochonToken
if data.Password != "" { if data.Password != "" {
// Update the user config // Update the user config
@ -107,6 +127,9 @@ func UpdateUserHandler(env *web.Env, w http.ResponseWriter, r *http.Request) err
log.Debugf("updating user") log.Debugf("updating user")
// Need to unsubscribe the user from all the eventers
events.Unsubscribe(user)
// Save the user with the new configurations // Save the user with the new configurations
if err := user.Update(env.Database); err != nil { if err := user.Update(env.Database); err != nil {
return err return err

View File

@ -6,10 +6,12 @@ import (
"net/http" "net/http"
"git.quimbo.fr/odwrtw/canape/backend/models" "git.quimbo.fr/odwrtw/canape/backend/models"
"github.com/gofrs/uuid"
"github.com/sirupsen/logrus" "github.com/sirupsen/logrus"
) )
type ipContextKey string type ipContextKey string
type requestIDContextKey string
type authContextKey string type authContextKey string
// Middleware get User from session and put it in context // 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 { func getIPFromRequest(r *http.Request) string {
return r.Context().Value(ipContextKey("ip")).(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 ""
}

View File

@ -4,13 +4,15 @@ import (
"net" "net"
"time" "time"
"git.quimbo.fr/odwrtw/canape/backend/models"
"github.com/gorilla/websocket" "github.com/gorilla/websocket"
"github.com/jmoiron/sqlx"
"github.com/sirupsen/logrus" "github.com/sirupsen/logrus"
) )
type channel struct { // Channel represents the channel of the user and the server
log *logrus.Entry type Channel struct {
events map[string]Eventer log *logrus.Entry
// Channel where the eventer will write messages to // Channel where the eventer will write messages to
serverEventStream chan ServerEvent serverEventStream chan ServerEvent
// Channel where the eventer will write errors to // Channel where the eventer will write errors to
@ -19,10 +21,16 @@ type channel struct {
done chan struct{} done chan struct{}
// Underlying ws connection // Underlying ws connection
conn *websocket.Conn 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 // 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 // Create the ping timer that will ping the client every pingWait seconds
// to check that he's still listening // to check that he's still listening
pingTicker := time.NewTicker(pingWait) pingTicker := time.NewTicker(pingWait)
@ -60,7 +68,7 @@ func (c *channel) writer() {
} }
// go routine reading messages from the websocket connection // go routine reading messages from the websocket connection
func (c *channel) reader() { func (c *Channel) reader() {
// Read loop // Read loop
c.conn.SetReadDeadline(time.Now().Add(pongWait)) c.conn.SetReadDeadline(time.Now().Add(pongWait))
for { for {
@ -93,7 +101,7 @@ func (c *channel) reader() {
return return
} }
e, ok := c.events[msg.Message] e, ok := Eventers[msg.Message]
if !ok { if !ok {
c.log.Warnf("no such event to subscribe %q", msg.Message) c.log.Warnf("no such event to subscribe %q", msg.Message)
continue continue
@ -102,21 +110,71 @@ func (c *channel) reader() {
switch msg.Type { switch msg.Type {
case "subscribe": case "subscribe":
c.log.Debugf("subscribe to %s", msg.Message) 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": case "unsubscribe":
c.log.Debugf("unsubscribe from %s", msg.Message) 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: default:
c.log.Warnf("invalid type: %s", msg.Type) c.log.Warnf("invalid type: %s", msg.Type)
} }
} }
} }
func (c *channel) close() { func (c *Channel) close() {
// Unsubscribe from all events // Unsubscribe from all events
for _, e := range c.events { for eventName := range c.events {
e.Unsubscribe() Eventers[eventName].Unsubscribe(c)
} }
// Tell the writer to stop // Tell the writer to stop
c.done <- struct{}{} 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)
}
}

View File

@ -1,11 +1,15 @@
package events package events
import ( import (
"encoding/json"
"fmt"
"net/http" "net/http"
"time" "time"
"git.quimbo.fr/odwrtw/canape/backend/auth" "git.quimbo.fr/odwrtw/canape/backend/auth"
"git.quimbo.fr/odwrtw/canape/backend/models"
"git.quimbo.fr/odwrtw/canape/backend/web" "git.quimbo.fr/odwrtw/canape/backend/web"
"github.com/gorilla/mux"
"github.com/gorilla/websocket" "github.com/gorilla/websocket"
) )
@ -18,6 +22,12 @@ const (
writeWait = 30 * time.Second 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 // WsHandler handles the websockets messages
func WsHandler(env *web.Env, w http.ResponseWriter, r *http.Request) error { func WsHandler(env *web.Env, w http.ResponseWriter, r *http.Request) error {
// Get the user // 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 // Channel where the eventers will write errors to
serverErrorStream := make(chan ServerError) serverErrorStream := make(chan ServerError)
events := map[string]Eventer{ requestID := auth.GetRequestIDFromRequest(r)
"torrents": &TorrentNotifier{
Notifier: NewNotifier(serverEventStream, serverErrorStream),
user: user,
log: env.Log,
},
}
c := channel{ c := Channel{
serverEventStream: serverEventStream, serverEventStream: serverEventStream,
serverErrorStream: serverErrorStream, serverErrorStream: serverErrorStream,
done: make(chan struct{}, 1), done: make(chan struct{}, 1),
conn: ws, conn: ws,
log: env.Log, 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 // 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 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")
}

View File

@ -1,9 +1,5 @@
package events package events
import (
"sync"
)
// ErrorLevel is the level of the ServerError // ErrorLevel is the level of the ServerError
type ErrorLevel string type ErrorLevel string
@ -16,8 +12,9 @@ const (
WarningError ErrorLevel = "warning" WarningError ErrorLevel = "warning"
FatalError ErrorLevel = "fatal" FatalError ErrorLevel = "fatal"
// Statuses // Statuses
OK Status = "ok" OK Status = "ok"
Error Status = "error" Disconnected Status = "disconnected"
Error Status = "error"
) )
// Event is the base of a message // Event is the base of a message
@ -52,90 +49,10 @@ type ServerError struct {
// Eventer define an interface that any eventer must follow // Eventer define an interface that any eventer must follow
type Eventer interface { type Eventer interface {
Subscribe(func()) Unsubscribe(chanl *Channel) error
Unsubscribe() Append(chanl *Channel)
Launch() Subscribers() []*Channel
Name() string NotifyAll(data interface{})
} Launch() error
Finish()
// 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
} }

193
backend/events/polochons.go Normal file
View File

@ -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
}
}

View File

@ -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{}{}
}

View File

@ -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
}

View File

@ -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() {
}

View File

@ -59,7 +59,7 @@ func GetMovies(env *web.Env, user *models.User, source string, category string)
} }
// Create a papi client // Create a papi client
client, err := user.NewPapiClient() client, err := user.NewPapiClient(env.Database)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -119,7 +119,7 @@ func GetShows(env *web.Env, user *models.User, source string, category string, f
} }
// Create a papi client // Create a papi client
client, err := user.NewPapiClient() client, err := user.NewPapiClient(env.Database)
if err != nil { if err != nil {
return nil, err return nil, err
} }

View File

@ -6,9 +6,9 @@ import (
"os" "os"
"git.quimbo.fr/odwrtw/canape/backend/auth" "git.quimbo.fr/odwrtw/canape/backend/auth"
"git.quimbo.fr/odwrtw/canape/backend/models"
"git.quimbo.fr/odwrtw/canape/backend/config" "git.quimbo.fr/odwrtw/canape/backend/config"
extmedias "git.quimbo.fr/odwrtw/canape/backend/external_medias" 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/ratings"
"git.quimbo.fr/odwrtw/canape/backend/web" "git.quimbo.fr/odwrtw/canape/backend/web"
@ -97,6 +97,8 @@ func main() {
n := negroni.Classic() n := negroni.Classic()
// Middleware for setting ips // Middleware for setting ips
n.Use(auth.NewIPMiddleware(log)) n.Use(auth.NewIPMiddleware(log))
// Middleware for request id
n.Use(auth.NewRequestIDMiddleware(log))
// Middleware for authentication // Middleware for authentication
n.Use(authMiddleware) n.Use(authMiddleware)
// Serve static files // Serve static files

View File

@ -2,10 +2,12 @@ package models
import ( import (
"database/sql" "database/sql"
"errors"
"fmt" "fmt"
"git.quimbo.fr/odwrtw/canape/backend/sqly" "git.quimbo.fr/odwrtw/canape/backend/sqly"
"github.com/jmoiron/sqlx" "github.com/jmoiron/sqlx"
"github.com/odwrtw/papi"
) )
const ( const (
@ -15,7 +17,7 @@ const (
RETURNING id;` RETURNING id;`
getPolochonQuery = `SELECT * FROM polochons WHERE name=$1;` getPolochonQuery = `SELECT * FROM polochons WHERE name=$1;`
getPolochonByIDQuery = `SELECT * FROM polochons WHERE id=$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 updatePolochonQuery = `UPDATE polochons SET
name=:name, url=:url, token=:token, auth_token=:auth_token, name=:name, url=:url, token=:token, auth_token=:auth_token,
admin_id=:admin_id admin_id=:admin_id
@ -23,10 +25,10 @@ const (
RETURNING *;` RETURNING *;`
deletePolochonQuery = `DELETE FROM polochons WHERE id=:id;` 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") var ErrUnknownPolochon = fmt.Errorf("polochons: polochon does'nt exist")
// Polochon represents a polochon // Polochon represents a polochon
@ -41,20 +43,7 @@ type Polochon struct {
Users []*User `json:"users"` Users []*User `json:"users"`
} }
// Get returns polochon with specified name // GetPolochonByID returns polochon using its id
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
func GetPolochonByID(q sqlx.Queryer, id string) (*Polochon, error) { func GetPolochonByID(q sqlx.Queryer, id string) (*Polochon, error) {
p := &Polochon{} p := &Polochon{}
err := q.QueryRowx(getPolochonByIDQuery, id).StructScan(p) err := q.QueryRowx(getPolochonByIDQuery, id).StructScan(p)
@ -67,7 +56,7 @@ func GetPolochonByID(q sqlx.Queryer, id string) (*Polochon, error) {
return p, nil return p, nil
} }
// GetAll returns all the polochons // GetAllPolochons returns all the polochons
func GetAllPolochons(db *sqlx.DB) ([]*Polochon, error) { func GetAllPolochons(db *sqlx.DB) ([]*Polochon, error) {
polochons := []*Polochon{} polochons := []*Polochon{}
err := db.Select(&polochons, getAllPolochonsQuery) err := db.Select(&polochons, getAllPolochonsQuery)
@ -77,7 +66,7 @@ func GetAllPolochons(db *sqlx.DB) ([]*Polochon, error) {
return polochons, nil 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) { func GetAllPolochonsByUser(db *sqlx.DB, id string) ([]*Polochon, error) {
polochons := []*Polochon{} polochons := []*Polochon{}
err := db.Select(&polochons, getPolochonsByUserIDQuery, id) err := db.Select(&polochons, getPolochonsByUserIDQuery, id)
@ -118,3 +107,16 @@ func (p *Polochon) Delete(ex *sqlx.DB) error {
} }
return nil 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
}

View File

@ -2,11 +2,9 @@ package models
import ( import (
"database/sql" "database/sql"
"encoding/json"
"errors" "errors"
"fmt" "fmt"
"git.quimbo.fr/odwrtw/canape/backend/config"
"git.quimbo.fr/odwrtw/canape/backend/sqly" "git.quimbo.fr/odwrtw/canape/backend/sqly"
"github.com/jmoiron/sqlx" "github.com/jmoiron/sqlx"
"github.com/jmoiron/sqlx/types" "github.com/jmoiron/sqlx/types"
@ -53,73 +51,23 @@ type User struct {
Activated bool `json:"activated"` Activated bool `json:"activated"`
PolochonID sql.NullString `json:"polochon_id" db:"polochon_id"` PolochonID sql.NullString `json:"polochon_id" db:"polochon_id"`
PolochonActivated bool `json:"polochon_activated" db:"polochon_activated"` PolochonActivated bool `json:"polochon_activated" db:"polochon_activated"`
} Polochon *Polochon `json:"polochon"`
// 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)
} }
// NewPapiClient creates a new papi client for the given user // NewPapiClient creates a new papi client for the given user
func (u *User) NewPapiClient() (*papi.Client, error) { func (u *User) NewPapiClient(db *sqlx.DB) (*papi.Client, error) {
var polochonConfig config.UserPolochon polochon, err := u.GetPolochon(db)
err := u.GetConfig("polochon", &polochonConfig)
if err != nil { 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 { if err != nil {
return nil, errors.New("error getting papi client") return nil, errors.New("error getting papi client")
} }
if polochonConfig.Token != "" { if u.Token != "" {
client.SetToken(polochonConfig.Token) client.SetToken(u.Token)
} }
return client, nil return client, nil
} }
@ -219,3 +167,8 @@ func (u *User) IsAdmin() bool {
func (u *User) IsActivated() bool { func (u *User) IsActivated() bool {
return u.Activated return u.Activated
} }
// GetPolochon returns the user's polochon
func (u *User) GetPolochon(db *sqlx.DB) (*Polochon, error) {
return GetPolochonByID(db, u.PolochonID.String)
}

View File

@ -6,7 +6,6 @@ import (
"net/http" "net/http"
"git.quimbo.fr/odwrtw/canape/backend/auth" "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/models"
"git.quimbo.fr/odwrtw/canape/backend/subtitles" "git.quimbo.fr/odwrtw/canape/backend/subtitles"
"git.quimbo.fr/odwrtw/canape/backend/web" "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) 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 // Get details with the polochon Detailer for each polochon.Movies we have
for _, m := range movies { for _, m := range movies {
// First try from the db // 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) user := auth.GetCurrentUser(r, env.Log)
// Create a new papi client // Create a new papi client
client, err := user.NewPapiClient() client, err := user.NewPapiClient(env.Database)
if err != nil { if err != nil {
return env.RenderError(w, err) 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) user := auth.GetCurrentUser(r, env.Log)
// Create a new papi client // Create a new papi client
client, err := user.NewPapiClient() client, err := user.NewPapiClient(env.Database)
if err != nil { if err != nil {
return env.RenderError(w, err) 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) user := auth.GetCurrentUser(r, env.Log)
// Create a new papi client // Create a new papi client
client, err := user.NewPapiClient() client, err := user.NewPapiClient(env.Database)
if err != nil { if err != nil {
return env.RenderError(w, err) 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) user := auth.GetCurrentUser(r, env.Log)
// Create a new papi client // Create a new papi client
client, err := user.NewPapiClient() client, err := user.NewPapiClient(env.Database)
if err != nil { if err != nil {
return env.RenderError(w, err) 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) user := auth.GetCurrentUser(r, env.Log)
// Create a new papi client // Create a new papi client
client, err := user.NewPapiClient() client, err := user.NewPapiClient(env.Database)
if err != nil { if err != nil {
return env.RenderError(w, err) 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) user := auth.GetCurrentUser(r, env.Log)
// Create a new papi client // Create a new papi client
client, err := user.NewPapiClient() client, err := user.NewPapiClient(env.Database)
if err != nil { if err != nil {
return env.RenderError(w, err) return env.RenderError(w, err)
} }

View File

@ -254,7 +254,7 @@ func getPolochonMovies(user *models.User, env *web.Env) ([]*Movie, error) {
movies := []*Movie{} movies := []*Movie{}
// Create a papi client // Create a papi client
client, err := user.NewPapiClient() client, err := user.NewPapiClient(env.Database)
if err != nil { if err != nil {
return movies, err return movies, err
} }

View File

@ -164,28 +164,20 @@ func EditPolochonHandler(env *web.Env, w http.ResponseWriter, r *http.Request) e
return env.RenderOK(w, "Polochon updated") return env.RenderOK(w, "Polochon updated")
} }
// PolochonDeactivateUserHandler handles the users of a polochon // PolochonUserHandler edit a user's polochon config
func PolochonDeactivateUserHandler(env *web.Env, w http.ResponseWriter, r *http.Request) error { func PolochonUserHandler(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 {
log := env.Log.WithFields(logrus.Fields{ log := env.Log.WithFields(logrus.Fields{
"function": "polochons.PolochonUserHandler", "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) 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) 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")
} }

View File

@ -2,9 +2,9 @@ package main
import ( import (
admin "git.quimbo.fr/odwrtw/canape/backend/admins" admin "git.quimbo.fr/odwrtw/canape/backend/admins"
"git.quimbo.fr/odwrtw/canape/backend/models"
"git.quimbo.fr/odwrtw/canape/backend/events" "git.quimbo.fr/odwrtw/canape/backend/events"
extmedias "git.quimbo.fr/odwrtw/canape/backend/external_medias" 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/movies"
"git.quimbo.fr/odwrtw/canape/backend/polochons" "git.quimbo.fr/odwrtw/canape/backend/polochons"
"git.quimbo.fr/odwrtw/canape/backend/ratings" "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") env.Handle("/users/polochons", polochons.GetPolochonsHandler).WithRole(models.UserRole).Methods("GET")
// Polochon's route // 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", polochons.NewPolochonHandler).WithRole(models.UserRole).Methods("POST")
env.Handle("/polochons/{id}", polochons.EditPolochonHandler).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}", polochons.DeletePolochonHandler).WithRole(models.UserRole).Methods("DELETE")
env.Handle("/polochons/{id}/users/{user_id}", polochons.PolochonDeactivateUserHandler).WithRole(models.UserRole).Methods("DELETE") env.Handle("/polochons/{id}/users/{user_id}", polochons.PolochonUserHandler).WithRole(models.UserRole).Methods("POST")
// Movies routes // Movies routes
env.Handle("/movies/polochon", movies.PolochonMoviesHandler).WithRole(models.UserRole).Methods("GET") 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 // Route to handle websocket events
env.Handle("/events", events.WsHandler).WithRole(models.UserRole).Methods("GET") 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 // Admin routes
env.Handle("/admins/users", admin.GetUsersHandler).WithRole(models.AdminRole).Methods("GET") env.Handle("/admins/users", admin.GetUsersHandler).WithRole(models.AdminRole).Methods("GET")
env.Handle("/admins/users", admin.UpdateUserHandler).WithRole(models.AdminRole).Methods("POST") env.Handle("/admins/users", admin.UpdateUserHandler).WithRole(models.AdminRole).Methods("POST")

View File

@ -28,7 +28,7 @@ func GetDetailsHandler(env *web.Env, w http.ResponseWriter, r *http.Request) err
user := auth.GetCurrentUser(r, env.Log) user := auth.GetCurrentUser(r, env.Log)
client, err := user.NewPapiClient() client, err := user.NewPapiClient(env.Database)
if err != nil { if err != nil {
return env.RenderError(w, err) 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) user := auth.GetCurrentUser(r, env.Log)
client, err := user.NewPapiClient() client, err := user.NewPapiClient(env.Database)
if err != nil { if err != nil {
return env.RenderError(w, err) 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) 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 { if err != nil {
return env.RenderError(w, err) 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 { func GetWishlistHandler(env *web.Env, w http.ResponseWriter, r *http.Request) error {
user := auth.GetCurrentUser(r, env.Log) user := auth.GetCurrentUser(r, env.Log)
client, err := user.NewPapiClient() client, err := user.NewPapiClient(env.Database)
if err != nil { if err != nil {
return env.RenderError(w, err) 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) user := auth.GetCurrentUser(r, env.Log)
client, err := user.NewPapiClient() client, err := user.NewPapiClient(env.Database)
if err != nil { if err != nil {
return env.RenderError(w, err) 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) user := auth.GetCurrentUser(r, env.Log)
// Create a new papi client // Create a new papi client
client, err := user.NewPapiClient() client, err := user.NewPapiClient(env.Database)
if err != nil { if err != nil {
return env.RenderError(w, err) 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) user := auth.GetCurrentUser(r, env.Log)
// Create a new papi client // Create a new papi client
client, err := user.NewPapiClient() client, err := user.NewPapiClient(env.Database)
if err != nil { if err != nil {
return env.RenderError(w, err) return env.RenderError(w, err)
} }

View File

@ -200,7 +200,7 @@ func (s *Show) imgFile(imgType string) string {
func getPolochonShows(env *web.Env, user *models.User) ([]*Show, error) { func getPolochonShows(env *web.Env, user *models.User) ([]*Show, error) {
shows := []*Show{} shows := []*Show{}
client, err := user.NewPapiClient() client, err := user.NewPapiClient(env.Database)
if err != nil { if err != nil {
return shows, err return shows, err
} }

View File

@ -29,7 +29,7 @@ func DownloadHandler(env *web.Env, w http.ResponseWriter, r *http.Request) error
user := auth.GetCurrentUser(r, env.Log) user := auth.GetCurrentUser(r, env.Log)
client, err := user.NewPapiClient() client, err := user.NewPapiClient(env.Database)
if err != nil { if err != nil {
return env.RenderError(w, err) 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 { func ListHandler(env *web.Env, w http.ResponseWriter, r *http.Request) error {
user := auth.GetCurrentUser(r, env.Log) user := auth.GetCurrentUser(r, env.Log)
client, err := user.NewPapiClient() client, err := user.NewPapiClient(env.Database)
if err != nil { if err != nil {
return env.RenderError(w, err) 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) vars := mux.Vars(r)
torrentID := vars["id"] torrentID := vars["id"]
client, err := user.NewPapiClient() client, err := user.NewPapiClient(env.Database)
if err != nil { if err != nil {
return env.RenderError(w, err) return env.RenderError(w, err)
} }

View File

@ -8,6 +8,7 @@ import (
"git.quimbo.fr/odwrtw/canape/backend/auth" "git.quimbo.fr/odwrtw/canape/backend/auth"
"git.quimbo.fr/odwrtw/canape/backend/config" "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/models"
"git.quimbo.fr/odwrtw/canape/backend/tokens" "git.quimbo.fr/odwrtw/canape/backend/tokens"
"git.quimbo.fr/odwrtw/canape/backend/web" "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 return err
} }
err = user.NewConfig()
if err != nil {
return err
}
if err = user.Add(e.Database); err != nil { if err = user.Add(e.Database); err != nil {
return err return err
} }
@ -116,7 +112,6 @@ func EditHandler(e *web.Env, w http.ResponseWriter, r *http.Request) error {
var data struct { var data struct {
PolochonID string `json:"polochon_id"` PolochonID string `json:"polochon_id"`
PolochonToken string `json:"polochon_token"`
Password string `json:"password"` Password string `json:"password"`
PasswordConfirm string `json:"password_confirm"` 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 { if data.PolochonID != "" && user.PolochonID.String != data.PolochonID {
e.Log.Info("unsubscribing user...")
_, err := models.GetPolochonByID(e.Database, data.PolochonID) _, err := models.GetPolochonByID(e.Database, data.PolochonID)
if err != nil { if err != nil {
return e.RenderError(w, fmt.Errorf("Could not find such polochon")) 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{ user.PolochonID = sql.NullString{
String: data.PolochonID, String: data.PolochonID,
Valid: true, Valid: true,
} }
user.PolochonActivated = false user.PolochonActivated = false
}
user.Token = data.PolochonToken }
// Save the user with the new configurations // Save the user with the new configurations
if err := user.Update(e.Database); err != nil { 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) user := auth.GetCurrentUser(r, e.Log)
// Create a papi client // Create a papi client
client, err := user.NewPapiClient() client, err := user.NewPapiClient(e.Database)
if err != nil { if err != nil {
return e.RenderError(w, fmt.Errorf("error while getting user")) return e.RenderError(w, fmt.Errorf("error while getting user"))
} }

3
go.mod
View File

@ -5,12 +5,15 @@ go 1.12
require ( require (
github.com/dgrijalva/jwt-go v3.2.0+incompatible github.com/dgrijalva/jwt-go v3.2.0+incompatible
github.com/go-sql-driver/mysql v1.4.1 // indirect 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/mux v1.7.1
github.com/gorilla/websocket v1.4.0 github.com/gorilla/websocket v1.4.0
github.com/gregdel/srt2vtt v0.0.0-20170314031115-46562d19ab2d github.com/gregdel/srt2vtt v0.0.0-20170314031115-46562d19ab2d
github.com/jmoiron/sqlx v1.2.0 github.com/jmoiron/sqlx v1.2.0
github.com/kr/pretty v0.1.0
github.com/lib/pq v1.1.1 github.com/lib/pq v1.1.1
github.com/mattn/go-sqlite3 v1.10.0 // indirect 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/nfnt/resize v0.0.0-20180221191011-83c6a9932646
github.com/odwrtw/papi v0.0.0-20190511132159-936937ad8b6a github.com/odwrtw/papi v0.0.0-20190511132159-936937ad8b6a
github.com/odwrtw/polochon v0.0.0-20190510120140-0141d4124793 github.com/odwrtw/polochon v0.0.0-20190510120140-0141d4124793

3
go.sum
View File

@ -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.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 h1:g24URVg0OFbNUTx9qqY1IRZ9D9z3iPyi5zKhQZpNwpA=
github.com/go-sql-driver/mysql v1.4.1/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG5ZlKdlhCg5w= 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/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 h1:Dw4jY2nghMMRsh1ol8dv1axHkDwMQK2DHerMNJsIpJU=
github.com/gorilla/mux v1.7.1/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs= 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.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 h1:jbhqpg7tQe4SupckyijYiy0mJJ/pRyHvXf7JdWK860o=
github.com/mattn/go-sqlite3 v1.10.0/go.mod h1:FPy6KqzDD04eiIsT53CuJW3U88zkxoIYsOqkbpncsNc= 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/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 h1:fmNYVwqnSfB9mZU6OS2O6GsXM+wcskZDuKQzvN1EDeE=
github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=