Add the Eventers logic and handlers
This commit is contained in:
parent
07eefe1870
commit
3b397300bc
@ -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
|
||||||
|
@ -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 ""
|
||||||
|
}
|
||||||
|
@ -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)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -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")
|
||||||
|
}
|
||||||
|
@ -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
193
backend/events/polochons.go
Normal 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
|
||||||
|
}
|
||||||
|
}
|
128
backend/events/torrent_eventer.go
Normal file
128
backend/events/torrent_eventer.go
Normal 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{}{}
|
||||||
|
}
|
@ -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
|
|
||||||
}
|
|
41
backend/events/video_eventer.go
Normal file
41
backend/events/video_eventer.go
Normal 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() {
|
||||||
|
}
|
@ -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
|
||||||
}
|
}
|
||||||
|
@ -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
|
||||||
|
@ -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
|
||||||
|
}
|
||||||
|
@ -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)
|
||||||
|
}
|
||||||
|
@ -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)
|
||||||
}
|
}
|
||||||
|
@ -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
|
||||||
}
|
}
|
||||||
|
@ -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")
|
||||||
}
|
}
|
||||||
|
@ -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")
|
||||||
|
@ -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)
|
||||||
}
|
}
|
||||||
|
@ -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
|
||||||
}
|
}
|
||||||
|
@ -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)
|
||||||
}
|
}
|
||||||
|
@ -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
3
go.mod
@ -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
3
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.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=
|
||||||
|
Loading…
x
Reference in New Issue
Block a user