From 0f7513709797136735055fc19c47c24e3bd94180 Mon Sep 17 00:00:00 2001 From: Brian Buller Date: Mon, 22 Feb 2016 21:23:53 -0600 Subject: [PATCH] Working on server app --- boltrest-server/about_screen.go | 21 +++++++++ boltrest-server/main.go | 74 +++++++++++++++++++++++++++++- boltrest-server/screen.go | 42 +++++++++++++++++ boltrest-server/server_screen.go | 75 +++++++++++++++++++++++++++++++ boltrest-server/web_server.go | 77 ++++++++++++++++++++++++++++++++ boltrest.go | 39 +++++++++++----- 6 files changed, 315 insertions(+), 13 deletions(-) create mode 100644 boltrest-server/about_screen.go create mode 100644 boltrest-server/screen.go create mode 100644 boltrest-server/server_screen.go create mode 100644 boltrest-server/web_server.go diff --git a/boltrest-server/about_screen.go b/boltrest-server/about_screen.go new file mode 100644 index 0000000..3626216 --- /dev/null +++ b/boltrest-server/about_screen.go @@ -0,0 +1,21 @@ +package main + +import ( + "github.com/br0xen/termbox-util" + "github.com/nsf/termbox-go" +) + +// AboutScreen is the about screen +type AboutScreen struct{} + +func (screen *AboutScreen) handleEvent(event termbox.Event) int { + return ServerScreenIndex +} + +func (screen *AboutScreen) performLayout(style Style) {} + +func (screen *AboutScreen) drawScreen(style Style) { + width, height := termbox.Size() + exitTxt := "Press any key to exit help" + termboxUtil.DrawStringAtPoint(exitTxt, (width-len(exitTxt))/2, height-1, style.titleFg, style.titleBg) +} diff --git a/boltrest-server/main.go b/boltrest-server/main.go index dad8d1d..421e689 100644 --- a/boltrest-server/main.go +++ b/boltrest-server/main.go @@ -4,8 +4,11 @@ import ( "errors" "math/rand" "os" + "syscall" + "time" "github.com/br0xen/boltrest" + "github.com/nsf/termbox-go" ) var adminDB *boltrest.DB @@ -13,10 +16,60 @@ var adminDB *boltrest.DB func main() { initialize() adminDB = boltrest.Open("admin.db") + + err := termbox.Init() + if err != nil { + panic(err) + } + defer termbox.Close() + stl := getDefaultStyle() + termbox.SetOutputMode(termbox.Output256) + mainLoop(stl) } func mainLoop() { - + screens := defaultScreens() + displayScreen := screens[NasomiScreenIndex] + layoutAndDrawScreen(displayScreen, stl) + eventChan := make(chan termbox.Event) + go readUserInput(eventChan) + go sendNoneEvent(eventChan) + for { + event := <-eventChan + if event.Type == termbox.EventKey { + if event.Key == termbox.KeyCtrlZ { + process, _ := os.FindProcess(os.Getpid()) + termbox.Close() + process.Signal(syscall.SIGSTOP) + termbox.Init() + } else if event.Key == termbox.KeyCtrlC { + termbox.Close() + os.Exit(0) + } + newScreenIndex := displayScreen.handleKeyEvent(event) + if newScreenIndex < len(screens) { + displayScreen = screens[newScreenIndex] + layoutAndDrawScreen(displayScreen, stl) + } else { + termbox.Close() + break + } + } + if event.Type == termbox.EventResize || event.Type == termbox.EventNone { + layoutAndDrawScreen(displayScreen, stl) + } + } +} +func readUserInput(e chan termbox.Event) { + for { + e <- termbox.PollEvent() + } +} +func sendNoneEvent(e chan termbox.Event) { + for { + time.Sleep(time.Second) + e <- termbox.Event{Type: termbox.EventNone} + } } func initialize() error { @@ -42,6 +95,25 @@ func initialize() error { return nil } +const ( + // ServerScreenIndex is the index + ServerScreenIndex = iota + // AboutScreenIndex The idx number for the 'About' Screen + AboutScreenIndex + // ExitScreenIndex The idx number for Exiting + ExitScreenIndex +) + +func defaultScreens() []Screen { + serverScreen := ServerScreen{} + aboutScreen := AboutScreen{} + screens := []Screen{ + &serverScreen, + &aboutScreen, + } + return screens +} + const keyChars = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789" func generateAPIKey() string { diff --git a/boltrest-server/screen.go b/boltrest-server/screen.go new file mode 100644 index 0000000..a7b9d63 --- /dev/null +++ b/boltrest-server/screen.go @@ -0,0 +1,42 @@ +package main + +import "github.com/nsf/termbox-go" + +// Screen is a basic structure for all of the applications screens +type Screen interface { + handleKeyEvent(event termbox.Event) int + performLayout(style Style) + drawScreen(style Style) +} + +func drawBackground(bg termbox.Attribute) { + termbox.Clear(0, bg) +} + +func layoutAndDrawScreen(screen Screen, style Style) { + screen.performLayout(style) + drawBackground(style.defaultBg) + screen.drawScreen(style) + termbox.Flush() +} + +// Style defines style for a screen +type Style struct { + defaultBg termbox.Attribute + defaultFg termbox.Attribute + titleFg termbox.Attribute + titleBg termbox.Attribute + cursorFg termbox.Attribute + cursorBg termbox.Attribute +} + +func getDefaultStyle() Style { + return Style{ + defaultBg: termbox.ColorBlack, + defaultFg: termbox.ColorWhite, + titleBg: termbox.ColorBlack, + titleFg: termbox.ColorGreen, + cursorBg: termbox.ColorWhite, + cursorFg: termbox.ColorBlack, + } +} diff --git a/boltrest-server/server_screen.go b/boltrest-server/server_screen.go new file mode 100644 index 0000000..f73ac29 --- /dev/null +++ b/boltrest-server/server_screen.go @@ -0,0 +1,75 @@ +package main + +import ( + "time" + + "github.com/br0xen/termbox-util" + "github.com/nsf/termbox-go" +) + +// ServerScreen holds all that's going on :D +type ServerScreen struct { + initialized bool + + message string + messageTime time.Time + messageTimeout time.Duration + tabIdx int +} + +func (screen *ServerScreen) handleEvent(event termbox.Event) int { + _, h := termbox.Size() + if event.Type == termbox.EventKey { + } + return ServerScreenIndex +} + +func (screen *ServerScreen) performLayout(style Style) { + w, h := termbox.Size() + if !screen.initialized { + } + + if screen.messageTimeout > 0 && time.Since(screen.messageTime) > screen.messageTimeout { + screen.clearMessage() + msgString := "" + msgString = termboxUtil.AlignText(msgString, w, termboxUtil.AlignLeft) + screen.setMessage(msgString) + } +} + +func (screen *ServerScreen) drawScreen(style Style) { + w, h := termbox.Size() + screen.drawHeader(style) + screen.drawFooter(style) +} + +func (screen *ServerScreen) drawHeader(style Style) { + w, _ := termbox.Size() + ttl := termboxUtil.AlignTextWithFill("Boltrest Server", w+1, termboxUtil.AlignCenter, '=') + termboxUtil.DrawStringAtPoint(ttl, 0, 0, style.defaultBg, style.defaultFg) +} + +func (screen *ServerScreen) drawFooter(style Style) { + _, height := termbox.Size() + termboxUtil.DrawStringAtPoint(screen.message, 0, height-1, style.defaultBg, style.defaultFg) +} + +func (screen *ServerScreen) setMessage(msg string) { + screen.message = msg + screen.messageTime = time.Now() + screen.messageTimeout = -1 +} + +/* setMessageWithTimeout lets you specify the timeout for the message + * setting it to -1 means it won't timeout + */ +func (screen *ServerScreen) setMessageWithTimeout(msg string, timeout time.Duration) { + screen.message = msg + screen.messageTime = time.Now() + screen.messageTimeout = timeout +} + +func (screen *ServerScreen) clearMessage() { + screen.message = "" + screen.messageTimeout = -1 +} diff --git a/boltrest-server/web_server.go b/boltrest-server/web_server.go new file mode 100644 index 0000000..dca0011 --- /dev/null +++ b/boltrest-server/web_server.go @@ -0,0 +1,77 @@ +package main + +import ( + "encoding/json" + "log" + "net/http" + + "github.com/devict/magopie" + "github.com/devict/magopie/vendor/github.com/gorilla/mux" + "github.com/devict/magopie/vendor/github.com/justinas/alice" +) + +// WebServer ... +type WebServer struct { + addr string + tlsCert string + tlsKey string + + adminMode bool +} + +// Listen makes it listen for connetions +func (s *WebServer) Listen() { + if s.tlsCert != "" && s.tlsKey != "" { + log.Printf("Listening for HTTPS on %s with key %s and cert %s", s.addr, s.tlsKey, s.tlsCert) + log.Fatal(http.ListenAndServeTLS(s.addr, s.tlsCert, s.tlsKey, s.router(a))) + } else { + log.Printf("Listening for HTTP on %s", *addr) + log.Fatal(http.ListenAndServe(*addr, s.router(a))) + } +} + +// Router defines all of the routes +func (s *WebServer) Router() http.Handler { + r := mux.NewRouter() + + r.HandleFunc("/", s.handleAllSites).Methods("GET") + r.HandleFunc("/genAPIKey/{db}", s.genAPIKey).Methods("POST") + + chain := alice.New(mwLogger, mwAuthenticationCheck(a.key)).Then(r) + + return chain +} + +func (s *WebServer) handleRequest(w http.ResponseWriter, r *http.Request) { + err := json.NewEncoder(w).Encode() + if err != nil { + log.Println(err) + } +} + +func (s *WebServer) genAPIKey(w http.ResponseWriter, r *http.Request) { + err := json.NewEncoder(w).Encode() + if err != nil { + log.Println(err) + } +} + +func (s *WebServer) mwLogger(next http.Handler) http.Handler { + return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + log.Println("Serving", r.Method, r.URL.String(), "to", r.RemoteAddr) + next.ServeHTTP(w, r) + }) +} + +func (s *WebServer) mwAuthenticationCheck(key string) func(http.Handler) http.Handler { + return func(next http.Handler) http.Handler { + return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + if !magopie.CheckMAC(r.Header.Get("X-Request-ID"), r.Header.Get("X-HMAC"), key) { + log.Println("Request failed HMAC") + w.WriteHeader(http.StatusUnauthorized) + return + } + next.ServeHTTP(w, r) + }) + } +} diff --git a/boltrest.go b/boltrest.go index 9a3f29b..2fa0c5d 100644 --- a/boltrest.go +++ b/boltrest.go @@ -12,7 +12,7 @@ import ( "github.com/boltdb/bolt" ) -// This a custom library for saving/syncing data +// This is a custom library for saving/syncing data // in a local bolt db and a remote db through a // rest api. @@ -34,12 +34,20 @@ func Open(filename string) (*DB, error) { if err != nil { return nil, err } + // Go ahead and make sure it's fresh + b.RefreshDB() return &b, nil } +// Offline sets this DB to offline mode +// That means it won't try to sync anything +func (b *DB) Offline() { + b.online = false +} + // MkBucketPath builds all buckets in the string slice func (b *DB) MkBucketPath(path []string) error { - // TODO: Make sure local db is fresh (or offline) + b.RefreshDB() err := b.localDB.Update(func(tx *bolt.Tx) error { var err error bkt := tx.Bucket([]byte(path[0])) @@ -76,8 +84,7 @@ func (b *DB) MkBucketPath(path []string) error { func (b *DB) GetValue(path []string, key string) (string, error) { var err error var ret string - // TODO: Make sure local db is fresh (or offline) - + b.RefreshDB() b.localDB.View(func(tx *bolt.Tx) error { bkt := tx.Bucket([]byte(path[0])) if bkt == nil { @@ -100,6 +107,7 @@ func (b *DB) GetValue(path []string, key string) (string, error) { // SetValue sets the value at path to val // path is a slice of tokens func (b *DB) SetValue(path []string, key, val string) error { + b.RefreshDB() err := b.MkBucketPath(path) if err != nil { return err @@ -180,10 +188,10 @@ func (b *DB) SetTimestamp(path []string, key string, val time.Time) error { // GetBucketList returns a list of all sub-buckets at path func (b *DB) GetBucketList(path []string) ([]string, error) { + b.RefreshDB() + var err error var ret []string - // TODO: Make sure local db is fresh (or offline) - err = b.localDB.Update(func(tx *bolt.Tx) error { bkt := tx.Bucket([]byte(path[0])) if bkt == nil { @@ -216,10 +224,10 @@ func (b *DB) GetBucketList(path []string) ([]string, error) { // GetKeyList returns a list of all keys at path func (b *DB) GetKeyList(path []string) ([]string, error) { + b.RefreshDB() + var err error var ret []string - // TODO: Make sure local db is fresh (or offline) - err = b.localDB.Update(func(tx *bolt.Tx) error { bkt := tx.Bucket([]byte(path[0])) if bkt == nil { @@ -250,9 +258,8 @@ func (b *DB) GetKeyList(path []string) ([]string, error) { // DeletePair deletes the pair with key at path func (b *DB) DeletePair(path []string, key string) error { + b.RefreshDB() var err error - // TODO: Make sure local db is fresh (or offline) - err = b.localDB.Update(func(tx *bolt.Tx) error { bkt := tx.Bucket([]byte(path[0])) if bkt == nil { @@ -280,9 +287,8 @@ func (b *DB) DeletePair(path []string, key string) error { // DeleteBucket deletes the bucket key at path func (b *DB) DeleteBucket(path []string, key string) error { + b.RefreshDB() var err error - // TODO: Make sure local db is fresh (or offline) - err = b.localDB.Update(func(tx *bolt.Tx) error { bkt := tx.Bucket([]byte(path[0])) if bkt == nil { @@ -305,6 +311,15 @@ func (b *DB) DeleteBucket(path []string, key string) error { return err } +// RefreshDB makes sure that the DB is fresh with the server version +func (b *DB) RefreshDB() error { + var err error + if b.online { + // TODO: Compare latest change here with lates on server + // Then sync, if needed + } +} + type setURLResponse struct { dbMD5 string }