1
Fork 0

Work on notifications + custom logger

This commit is contained in:
viktorstrate 2020-02-21 20:51:50 +01:00
parent 2d15e7c41f
commit 935dc5b55e
8 changed files with 188 additions and 30 deletions

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

85
api/server/logging.go Normal file
View File

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

View File

@ -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])