Work on notifications + custom logger
This commit is contained in:
parent
2d15e7c41f
commit
935dc5b55e
|
@ -4,6 +4,7 @@ go 1.13
|
|||
|
||||
require (
|
||||
github.com/99designs/gqlgen v0.10.2
|
||||
github.com/fatih/color v1.9.0
|
||||
github.com/go-chi/chi v3.3.2+incompatible
|
||||
github.com/go-chi/cors v1.0.0
|
||||
github.com/go-sql-driver/mysql v1.5.0
|
||||
|
@ -18,6 +19,7 @@ require (
|
|||
github.com/rs/cors v1.6.0
|
||||
github.com/sirupsen/logrus v1.4.2
|
||||
github.com/vektah/gqlparser v1.2.0
|
||||
github.com/wsxiaoys/terminal v0.0.0-20160513160801-0940f3fc43a0
|
||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2
|
||||
golang.org/x/image v0.0.0-20200119044424-58c23975cae1
|
||||
)
|
||||
|
|
12
api/go.sum
12
api/go.sum
|
@ -6,6 +6,8 @@ github.com/andreyvit/diff v0.0.0-20170406064948-c7f18ee00883/go.mod h1:rCTlJbsFo
|
|||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/fatih/color v1.9.0 h1:8xPHl4/q1VyqGIPif1F+1V3Y3lSmrq01EabUW3CoW5s=
|
||||
github.com/fatih/color v1.9.0/go.mod h1:eQcE1qtQxscV5RaZvpXrrb8Drkc3/DdQ+uUYCNjL+zU=
|
||||
github.com/go-chi/chi v3.3.2+incompatible h1:uQNcQN3NsV1j4ANsPh42P4ew4t6rnRbJb8frvpp31qQ=
|
||||
github.com/go-chi/chi v3.3.2+incompatible/go.mod h1:eB3wogJHnLi3x/kFX2A+IbTBlXxmMeXJVKy9tTv1XzQ=
|
||||
github.com/go-chi/cors v1.0.0 h1:e6x8k7uWbUwYs+aXDoiUzeQFT6l0cygBYyNhD7/1Tg0=
|
||||
|
@ -36,6 +38,11 @@ github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
|
|||
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
|
||||
github.com/lib/pq v1.3.0 h1:/qkRGz8zljWiDcFvgpwUpwIAPu3r07TDvs3Rws+o/pU=
|
||||
github.com/lib/pq v1.3.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=
|
||||
github.com/mattn/go-colorable v0.1.4 h1:snbPLB8fVfU9iwbbo30TPtbLRzwWu6aJS6Xh4eaaviA=
|
||||
github.com/mattn/go-colorable v0.1.4/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE=
|
||||
github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s=
|
||||
github.com/mattn/go-isatty v0.0.11 h1:FxPOTFNqGkuDUGi3H/qkUbQO4ZiBa2brKq5r0l8TGeM=
|
||||
github.com/mattn/go-isatty v0.0.11/go.mod h1:PhnuNfih5lzO57/f3n+odYbM4JtupLOxQOAqxQCu2WE=
|
||||
github.com/mitchellh/mapstructure v0.0.0-20180203102830-a4e142e9c047 h1:zCoDWFD5nrJJVjbXiDZcVhOBSzKn3o9LgRLLMRNuru8=
|
||||
github.com/mitchellh/mapstructure v0.0.0-20180203102830-a4e142e9c047/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
|
||||
github.com/nf/cr2 v0.0.0-20180623103828-4699471a17ed h1:QP63yO3XEt8tJ1DgBsNjbOyBGjy2eHy9ITK4Eisr9rg=
|
||||
|
@ -67,6 +74,8 @@ github.com/vektah/dataloaden v0.2.1-0.20190515034641-a19b9a6e7c9e/go.mod h1:/HUd
|
|||
github.com/vektah/gqlparser v1.2.0 h1:ntkSCX7F5ZJKl+HIVnmLaO269MruasVpNiMOjX9kgo0=
|
||||
github.com/vektah/gqlparser v1.2.0/go.mod h1:bkVf0FX+Stjg/MHnm8mEyubuaArhNEqfQhF+OTiAL74=
|
||||
github.com/viktorstrate/photoview v0.0.0-20200119220544-691e4c7dc433 h1:n6jGnDctC9HI7B1rnc5ATQPYaaxQaJtS8bI6oI0QV34=
|
||||
github.com/wsxiaoys/terminal v0.0.0-20160513160801-0940f3fc43a0 h1:3UeQBvD0TFrlVjOeLOBz+CPAI8dnbqNSVwUwRrkp7vQ=
|
||||
github.com/wsxiaoys/terminal v0.0.0-20160513160801-0940f3fc43a0/go.mod h1:IXCdmsXIht47RaVFLEdVnh1t+pgYtTAhQGj73kz+2DM=
|
||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2 h1:VklqNMn3ovrHsnt90PveolxSbWFaJdECFbxSq0Mqo2M=
|
||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||
golang.org/x/image v0.0.0-20200119044424-58c23975cae1 h1:5h3ngYt7+vXCDZCup/HkCQgW5XwmSvR/nA2JmJ0RErg=
|
||||
|
@ -74,8 +83,11 @@ golang.org/x/image v0.0.0-20200119044424-58c23975cae1/go.mod h1:FeLwcggjj3mMvU+o
|
|||
golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190422165155-953cdadca894 h1:Cz4ceDQGXuKRnVBDTS23GTn/pU5OE2C0WrNTOYK1Uuc=
|
||||
golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20191026070338-33540a1f6037 h1:YyJpGZS1sBuBCzLAR1VEpK193GlqGZbnPFnPV/5Rsb4=
|
||||
golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/tools v0.0.0-20190125232054-d66bd3c5d5a6/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
golang.org/x/tools v0.0.0-20190515012406-7d7faa4812bd h1:oMEQDWVXVNpceQoVd1JN3CQ7LYJJzs5qWqZIUcxXHHw=
|
||||
|
|
|
@ -7,7 +7,7 @@ import (
|
|||
"sync"
|
||||
)
|
||||
|
||||
type NotificationChannel = <-chan *models.Notification
|
||||
type NotificationChannel = chan<- *models.Notification
|
||||
|
||||
type NotificationListener struct {
|
||||
listenerID int
|
||||
|
@ -44,6 +44,9 @@ func DeregisterListener(listenerID int) error {
|
|||
defer notificationLock.Unlock()
|
||||
|
||||
for i, listener := range notificationListeners {
|
||||
|
||||
log.Println("Deregistering notification listener")
|
||||
|
||||
if listener.listenerID == listenerID {
|
||||
|
||||
if len(notificationListeners) > 1 {
|
||||
|
@ -62,3 +65,16 @@ func DeregisterListener(listenerID int) error {
|
|||
|
||||
return errors.New("ListenerID not found, while trying to deregister it")
|
||||
}
|
||||
|
||||
func BroadcastNotification(notification *models.Notification) {
|
||||
|
||||
log.Println("Broadcasting notification")
|
||||
|
||||
notificationLock.Lock()
|
||||
defer notificationLock.Unlock()
|
||||
|
||||
for _, listener := range notificationListeners {
|
||||
listener.channel <- notification
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -4,17 +4,18 @@ import (
|
|||
"context"
|
||||
|
||||
"github.com/viktorstrate/photoview/api/graphql/auth"
|
||||
"github.com/viktorstrate/photoview/api/graphql/models"
|
||||
"github.com/viktorstrate/photoview/api/graphql/notification"
|
||||
)
|
||||
|
||||
func (r *subscriptionResolver) Notification(ctx context.Context) (notification.NotificationChannel, error) {
|
||||
func (r *subscriptionResolver) Notification(ctx context.Context) (<-chan *models.Notification, error) {
|
||||
|
||||
user := auth.UserFromContext(ctx)
|
||||
if user == nil {
|
||||
return nil, auth.ErrUnauthorized
|
||||
}
|
||||
|
||||
notificationChannel := make(notification.NotificationChannel, 1)
|
||||
notificationChannel := make(chan *models.Notification, 1)
|
||||
|
||||
listenerID := notification.RegisterListener(user, notificationChannel)
|
||||
|
||||
|
|
|
@ -13,6 +13,8 @@ import (
|
|||
|
||||
"github.com/h2non/filetype"
|
||||
"github.com/viktorstrate/photoview/api/graphql/models"
|
||||
"github.com/viktorstrate/photoview/api/graphql/notification"
|
||||
"github.com/viktorstrate/photoview/api/utils"
|
||||
)
|
||||
|
||||
type scanner_cache map[string]interface{}
|
||||
|
@ -75,6 +77,16 @@ func ScanUser(database *sql.DB, userId int) error {
|
|||
}
|
||||
|
||||
func scan(database *sql.DB, user *models.User) {
|
||||
|
||||
notifyKey := utils.GenerateToken()
|
||||
|
||||
notification.BroadcastNotification(&models.Notification{
|
||||
Key: notifyKey,
|
||||
Type: models.NotificationTypeMessage,
|
||||
Header: "User scan started",
|
||||
Content: "Scanning has started...",
|
||||
})
|
||||
|
||||
// Start scanning
|
||||
scanner_cache := make(scanner_cache)
|
||||
album_paths_scanned := make([]interface{}, 0)
|
||||
|
@ -179,6 +191,14 @@ func scan(database *sql.DB, user *models.User) {
|
|||
|
||||
cleanupCache(database, album_paths_scanned, user)
|
||||
|
||||
notification.BroadcastNotification(&models.Notification{
|
||||
Key: notifyKey,
|
||||
Type: models.NotificationTypeMessage,
|
||||
Header: "User scan completed",
|
||||
Content: "Scanning has been completed...",
|
||||
Positive: true,
|
||||
})
|
||||
|
||||
log.Println("Done scanning")
|
||||
}
|
||||
|
||||
|
|
|
@ -45,10 +45,9 @@ func main() {
|
|||
}
|
||||
|
||||
rootRouter := mux.NewRouter()
|
||||
|
||||
rootRouter.Use(auth.Middleware(db))
|
||||
|
||||
// router.Use(middleware.Logger)
|
||||
|
||||
rootRouter.Use(server.LoggingMiddleware)
|
||||
rootRouter.Use(server.CORSMiddleware(devMode))
|
||||
|
||||
graphqlResolver := resolvers.Resolver{Database: db}
|
||||
|
|
|
@ -0,0 +1,85 @@
|
|||
package server
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"errors"
|
||||
"fmt"
|
||||
"net"
|
||||
"net/http"
|
||||
"time"
|
||||
|
||||
"github.com/viktorstrate/photoview/api/graphql/auth"
|
||||
"github.com/wsxiaoys/terminal/color"
|
||||
)
|
||||
|
||||
func LoggingMiddleware(next http.Handler) http.Handler {
|
||||
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
start := time.Now()
|
||||
|
||||
statusWriter := newStatusResponseWriter(&w)
|
||||
next.ServeHTTP(statusWriter, r)
|
||||
|
||||
elapsed := time.Since(start)
|
||||
date := time.Now().Format("2006/01/02 15:04:05")
|
||||
|
||||
status := statusWriter.status
|
||||
var statusColor string
|
||||
switch {
|
||||
case status < 200:
|
||||
statusColor = color.Colorize("b")
|
||||
case status < 300:
|
||||
statusColor = color.Colorize("g")
|
||||
case status < 400:
|
||||
statusColor = color.Colorize("c")
|
||||
case status < 500:
|
||||
statusColor = color.Colorize("y")
|
||||
default:
|
||||
statusColor = color.Colorize("r")
|
||||
}
|
||||
|
||||
user := auth.UserFromContext(r.Context())
|
||||
userText := "unauthenticated"
|
||||
if user != nil {
|
||||
userText = color.Sprintf("@ruser: %s", user.Username)
|
||||
}
|
||||
|
||||
statusText := color.Sprintf("%s%s %d", statusColor, r.Method, status)
|
||||
requestText := fmt.Sprintf("%s%s", r.Host, r.URL.Path)
|
||||
durationText := color.Sprintf("@c%s", elapsed)
|
||||
|
||||
fmt.Printf("%s %s %s %s %s\n", date, statusText, requestText, durationText, userText)
|
||||
|
||||
})
|
||||
}
|
||||
|
||||
type statusResponseWriter struct {
|
||||
http.ResponseWriter
|
||||
status int
|
||||
hijacker http.Hijacker
|
||||
}
|
||||
|
||||
func newStatusResponseWriter(w *http.ResponseWriter) *statusResponseWriter {
|
||||
return &statusResponseWriter{
|
||||
ResponseWriter: *w,
|
||||
hijacker: (*w).(http.Hijacker),
|
||||
}
|
||||
}
|
||||
|
||||
func (w *statusResponseWriter) WriteHeader(status int) {
|
||||
w.status = status
|
||||
w.ResponseWriter.WriteHeader(status)
|
||||
}
|
||||
|
||||
func (w *statusResponseWriter) Write(b []byte) (int, error) {
|
||||
if w.status == 0 {
|
||||
w.status = 200
|
||||
}
|
||||
return w.ResponseWriter.Write(b)
|
||||
}
|
||||
|
||||
func (w *statusResponseWriter) Hijack() (net.Conn, *bufio.ReadWriter, error) {
|
||||
if w.hijacker == nil {
|
||||
return nil, nil, errors.New("http.Hijacker not implemented by underlying http.ResponseWriter")
|
||||
}
|
||||
return w.hijacker.Hijack()
|
||||
}
|
|
@ -38,35 +38,58 @@ const SubscriptionsHook = ({ messages, setMessages }) => {
|
|||
|
||||
if (!data) return
|
||||
|
||||
const update = data.scannerStatusUpdate
|
||||
const newMessages = [...messages]
|
||||
|
||||
if (update.success) {
|
||||
newMessages[0] = {
|
||||
key: 'primary',
|
||||
type: 'progress',
|
||||
props: {
|
||||
header: update.finished ? 'Synced' : 'Syncing',
|
||||
content: update.message,
|
||||
percent: update.progress,
|
||||
positive: update.finished,
|
||||
},
|
||||
}
|
||||
const msg = data.notification
|
||||
|
||||
if (!update.finished) newMessages[0].props.onDismiss = null
|
||||
} else {
|
||||
const key = Math.random().toString(26)
|
||||
newMessages.push({
|
||||
key,
|
||||
type: 'message',
|
||||
props: {
|
||||
header: 'Sync error',
|
||||
content: update.message,
|
||||
negative: true,
|
||||
},
|
||||
})
|
||||
const newNotification = {
|
||||
key: msg.key,
|
||||
type: msg.type.toLowerCase(),
|
||||
props: {
|
||||
header: msg.header,
|
||||
content: msg.content,
|
||||
negative: msg.negative,
|
||||
positive: msg.positive,
|
||||
},
|
||||
}
|
||||
|
||||
const notifyIndex = newMessages.findIndex(
|
||||
msg => msg.key == newNotification.key
|
||||
)
|
||||
if (notifyIndex != -1) {
|
||||
newMessages[notifyIndex] = newNotification
|
||||
} else {
|
||||
newMessages.push(newNotification)
|
||||
}
|
||||
|
||||
// const update = data.scannerStatusUpdate
|
||||
|
||||
// if (update.success) {
|
||||
// newMessages[0] = {
|
||||
// key: 'primary',
|
||||
// type: 'progress',
|
||||
// props: {
|
||||
// header: update.finished ? 'Synced' : 'Syncing',
|
||||
// content: update.message,
|
||||
// percent: update.progress,
|
||||
// positive: update.finished,
|
||||
// },
|
||||
// }
|
||||
|
||||
// if (!update.finished) newMessages[0].props.onDismiss = null
|
||||
// } else {
|
||||
// const key = Math.random().toString(26)
|
||||
// newMessages.push({
|
||||
// key,
|
||||
// type: 'message',
|
||||
// props: {
|
||||
// header: 'Sync error',
|
||||
// content: update.message,
|
||||
// negative: true,
|
||||
// },
|
||||
// })
|
||||
// }
|
||||
|
||||
setMessages(newMessages)
|
||||
}, [data, error])
|
||||
|
||||
|
|
Loading…
Reference in New Issue