From 3b6f5647b0689abf04be73c3cf00297051753435 Mon Sep 17 00:00:00 2001 From: Mitch Riedstra Date: Thu, 4 Mar 2021 18:50:47 -0500 Subject: Refactor. Pull most of the functions into methods off of an App struct. Kill most global variables. --- cmd/web/delete.go | 10 ++-- cmd/web/download.go | 8 ++-- cmd/web/index.go | 25 ++++------ cmd/web/install.go | 89 ++++++++++++++--------------------- cmd/web/main.go | 125 ++++++++++++++++++++++++++++---------------------- cmd/web/serve-self.go | 2 +- cmd/web/unix.go | 1 - cmd/web/windows.go | 3 +- 8 files changed, 124 insertions(+), 139 deletions(-) (limited to 'cmd') diff --git a/cmd/web/delete.go b/cmd/web/delete.go index 49a1326..7fa003e 100644 --- a/cmd/web/delete.go +++ b/cmd/web/delete.go @@ -5,7 +5,7 @@ import ( "net/http" ) -func gameDelete(w http.ResponseWriter, r *http.Request) { +func (a App) HandleDelete(w http.ResponseWriter, r *http.Request) { if unauthorizedIfNotLocal(w, r) { return } @@ -25,9 +25,9 @@ func gameDelete(w http.ResponseWriter, r *http.Request) { return } - Lib.Lock() - g, ok := Lib.Games[game] - Lib.Unlock() + a.Library.Lock() + g, ok := a.Library.Games[game] + a.Library.Unlock() if !ok { Logger.Printf("Missing: %s", game) http.Error(w, "Game is missing", 404) @@ -42,6 +42,6 @@ func gameDelete(w http.ResponseWriter, r *http.Request) { } Logger.Printf("Removed game: %s", game) - reloadLib() + a.LibraryReload() http.Redirect(w, r, "/", 302) } diff --git a/cmd/web/download.go b/cmd/web/download.go index f8ba057..0238c8c 100644 --- a/cmd/web/download.go +++ b/cmd/web/download.go @@ -9,13 +9,13 @@ import ( "github.com/gorilla/mux" ) -func gameDownloader(w http.ResponseWriter, r *http.Request) { +func (a *App) HandleDownload(w http.ResponseWriter, r *http.Request) { vars := mux.Vars(r) game := vars["game"] - Lib.Lock() - g, ok := Lib.Games[game] - Lib.Unlock() + a.Library.Lock() + g, ok := a.Library.Games[game] + a.Library.Unlock() if !ok { Logger.Printf("Missing: %s", game) http.Error(w, "Game is missing", 404) diff --git a/cmd/web/index.go b/cmd/web/index.go index b90faf8..7291af0 100644 --- a/cmd/web/index.go +++ b/cmd/web/index.go @@ -1,31 +1,22 @@ package main import ( - "html/template" "net/http" - _ "embed" "riedstra.dev/mitch/steam-export/steam" ) -var ( - //go:embed templates/index.html - indexTemplate string - - Templ = template.Must(template.New("index").Parse(indexTemplate)) -) - -func index(w http.ResponseWriter, r *http.Request) { +func (a *App) HandleIndex(w http.ResponseWriter, r *http.Request) { // During rendering of the template I believe it's // mutating during the sort of keys, so Lib no longer // is an RWMutex and we're just locking this as if // we're writing to it - Lib.Lock() - defer Lib.Unlock() - status.Lock() - defer status.Unlock() + a.Library.Lock() + defer a.Library.Unlock() + a.Status.Lock() + defer a.Status.Unlock() - err := Templ.ExecuteTemplate(w, "index", + err := a.Templates.ExecuteTemplate(w, "index", struct { Lib *steam.Library Info *statusInfo @@ -34,8 +25,8 @@ func index(w http.ResponseWriter, r *http.Request) { Port string Version string }{ - &Lib.Library, - status, + &a.Library.Library, + a.Status, isLocal(r.RemoteAddr), getHostIP(), getPort(), diff --git a/cmd/web/install.go b/cmd/web/install.go index fa1ea7a..ce8aa8d 100644 --- a/cmd/web/install.go +++ b/cmd/web/install.go @@ -9,44 +9,25 @@ import ( "os" "strconv" "strings" - "sync" "time" ) -type statusInfo struct { - sync.RWMutex - Running bool - Error error - Url string - Transferred int64 - Size int64 - Start *time.Time -} - -var ( - status = &statusInfo{ - Running: false, - } - - getPath = make(chan string) -) - -func statsHandler(w http.ResponseWriter, r *http.Request) { - status.RLock() - defer status.RUnlock() +func (a *App) HandleStats(w http.ResponseWriter, r *http.Request) { + a.Status.RLock() + defer a.Status.RUnlock() w.Header().Add("Content-type", "application/json") enc := json.NewEncoder(w) - err := enc.Encode(status) + err := enc.Encode(a.Status) if err != nil { - Logger.Println("While encoding status: ", err) + Logger.Println("While encoding Status: ", err) } return } -func installHttp(u string) error { +func (a *App) installHttp(u string) error { Logger.Println("Installer: loading from url") resp, err := http.Get(u) if err != nil { @@ -58,14 +39,14 @@ func installHttp(u string) error { return fmt.Errorf("Failed to convert estimated size header: %w", err) } - status.Lock() - status.Size = estSize - status.Unlock() + a.Status.Lock() + a.Status.Size = estSize + a.Status.Unlock() rdr, wrtr := io.Pipe() go func() { - err = Lib.Extract(rdr) + err = a.Library.Extract(rdr) if err != nil { Logger.Printf("Installer: extracting %s", err) } @@ -74,9 +55,9 @@ func installHttp(u string) error { var total int64 start := time.Now() - status.Lock() - status.Start = &start - status.Unlock() + a.Status.Lock() + a.Status.Start = &start + a.Status.Unlock() for { var n int64 n, err = io.CopyN(wrtr, resp.Body, 100*1024*1024) @@ -94,9 +75,9 @@ func installHttp(u string) error { Logger.Printf("Downloading from %s, Size: %s, %0.1f%% Done, Rate: %.2f mb/s", u, formatBytes(estSize), float64(total)/float64(estSize)*100, rate) - status.Lock() - status.Transferred = total - status.Unlock() + a.Status.Lock() + a.Status.Transferred = total + a.Status.Unlock() } if err == io.EOF { @@ -106,14 +87,14 @@ func installHttp(u string) error { return err } -func installPath(p string) error { +func (a *App) installPath(p string) error { Logger.Println("Installer: loading from filesystem") fh, err := os.Open(p) if err != nil { return fmt.Errorf("Installer: opening %w", err) } - err = Lib.Extract(fh) + err = a.Library.Extract(fh) if err != nil { return fmt.Errorf("Installer: opening %w", err) } @@ -121,34 +102,34 @@ func installPath(p string) error { return nil } -// installer handles installing games either from a local path or a -// remote URL -func installer(urls <-chan string) { +// installer runs in the background installing games either from a local path or +// a remote URL +func (a *App) installer() { var err error - for u := range urls { - status.Lock() + for u := range a.download { + a.Status.Lock() Logger.Printf("Installer: running for URI: %s", u) - status.Running = true - status.Url = u - status.Unlock() + a.Status.Running = true + a.Status.Url = u + a.Status.Unlock() if strings.HasPrefix(u, "http") { - err = installHttp(u) + err = a.installHttp(u) } else { - err = installPath(u) + err = a.installPath(u) } - status.Lock() - status.Running = false - status.Error = err + a.Status.Lock() + a.Status.Running = false + a.Status.Error = err Logger.Printf("Installer: Completed request %s Errors: %s", u, err) - status.Unlock() + a.Status.Unlock() - reloadLib() + a.LibraryReload() } } -func gameInstaller(w http.ResponseWriter, r *http.Request) { +func (a *App) HandleInstall(w http.ResponseWriter, r *http.Request) { if unauthorizedIfNotLocal(w, r) { return } @@ -179,7 +160,7 @@ func gameInstaller(w http.ResponseWriter, r *http.Request) { } Logger.Printf("Installer: Sending request for: %s to channel", uri) - getPath <- uri + a.download <- uri http.Redirect(w, r, "/", 302) } diff --git a/cmd/web/main.go b/cmd/web/main.go index 091d22f..f0c7489 100644 --- a/cmd/web/main.go +++ b/cmd/web/main.go @@ -1,9 +1,10 @@ package main import ( + "embed" "flag" - "io" "fmt" + "html/template" "log" "math/rand" "net" @@ -12,17 +13,55 @@ import ( "strings" "sync" "time" - "embed" "github.com/gorilla/mux" "riedstra.dev/mitch/steam-export/steam" ) +type App struct { + Library *steamLib + Status *statusInfo + + Templates *template.Template + + // Sending to this channel triggers the downloader in the background + download chan string +} + +func NewApp(libPath string) (*App, error) { + lib, err := steam.NewLibrary(libPath) + if err != nil { + return nil, err + } + + a := &App{ + Library: &steamLib{}, + Status: &statusInfo{}, + download: make(chan string), + } + + a.Library.Library = *lib + + a.Templates = template.Must(template.New("index").Parse(indexTemplate)) + + return a, nil +} + type steamLib struct { steam.Library sync.Mutex } +type statusInfo struct { + sync.RWMutex + Running bool + Error error + Url string + Transferred int64 + Size int64 + Start *time.Time +} + var ( Version = "Development" Logger = log.New(os.Stderr, "", log.LstdFlags) @@ -30,25 +69,31 @@ var ( //go:embed static/* embeddedStatic embed.FS - - Lib steamLib + //go:embed templates/index.html + indexTemplate string ) -func reloadLib() { +func (a *App) LibrarySet(path string) { Logger.Println("Starting library reload") - Lib.Lock() - defer Lib.Unlock() + a.Library.Lock() + defer a.Library.Unlock() var err error - l2, err := steam.NewLibrary(DefaultLib) + l2, err := steam.NewLibrary(path) if err != nil { - Logger.Printf("Error reopening library: %s", err) + Logger.Printf("Error reopening lib: %s", err) return } - Lib.Library = *l2 - Logger.Println("Done reloading library") + a.Library.Library = *l2 + Logger.Println("Done reloading lib") } -func setLibHandler(w http.ResponseWriter, r *http.Request) { +func (a *App) LibraryReload() { + cur := a.Library.Folder + a.LibrarySet(cur) + return +} + +func (a *App) HandleSetLib(w http.ResponseWriter, r *http.Request) { if unauthorizedIfNotLocal(w, r) { return } @@ -60,39 +105,12 @@ func setLibHandler(w http.ResponseWriter, r *http.Request) { return } - DefaultLib = r.Form.Get("path") - reloadLib() - http.Redirect(w, r, "/", 302) -} - -func staticHandler(w http.ResponseWriter, r *http.Request){ - /* - vars := mux.Vars(r) - - fn, ok := vars["fn"] - if !ok { - http.Error(w, "Not found", http.StatusNotFound) - return - } - */ - - fn := r.URL.Path - - fh, err := embeddedStatic.Open(fn) - if err != nil { - Logger.Printf("While reading embedded file: %s", err) - http.Error(w, "Not found", http.StatusNotFound) - return - } - defer fh.Close() + a.LibrarySet(r.Form.Get("path")) - _, err = io.Copy(w, fh) - if err != nil { - Logger.Printf("While sending static file: %s", err) - } + http.Redirect(w, r, "/", 302) } -func quitHandler(w http.ResponseWriter, r *http.Request) { +func (a *App) HandleQuit(w http.ResponseWriter, r *http.Request) { if unauthorizedIfNotLocal(w, r) { return } @@ -176,28 +194,25 @@ func main() { Logger.SetFlags(log.LstdFlags | log.Llongfile) } - var err error - var l *steam.Library - l, err = steam.NewLibrary(DefaultLib) + a, err := NewApp(DefaultLib) if err != nil { - Logger.Fatalf("While opening library path: %s", err) + Logger.Fatal(err) } - Lib.Library = *l - go installer(getPath) + go a.installer() r := mux.NewRouter() - r.HandleFunc("/quit", quitHandler) - r.HandleFunc("/setLib", setLibHandler) - r.HandleFunc("/delete", gameDelete) - r.HandleFunc("/install", gameInstaller) + r.HandleFunc("/quit", a.HandleQuit) + r.HandleFunc("/setLib", a.HandleSetLib) + r.HandleFunc("/delete", a.HandleDelete) + r.HandleFunc("/install", a.HandleInstall) r.HandleFunc("/steam-export-web.exe", serveSelf) - r.HandleFunc("/download/{game}", gameDownloader) - r.HandleFunc("/status", statsHandler) + r.HandleFunc("/download/{game}", a.HandleDownload) + r.HandleFunc("/status", a.HandleStats) r.PathPrefix("/static").Handler( http.FileServer(http.FS(embeddedStatic))) - r.HandleFunc("/", index) + r.HandleFunc("/", a.HandleIndex) s := http.Server{ Handler: r, diff --git a/cmd/web/serve-self.go b/cmd/web/serve-self.go index 103fbf1..8abb830 100644 --- a/cmd/web/serve-self.go +++ b/cmd/web/serve-self.go @@ -2,8 +2,8 @@ package main import ( "io" - "os" "net/http" + "os" ) func serveSelf(w http.ResponseWriter, r *http.Request) { diff --git a/cmd/web/unix.go b/cmd/web/unix.go index bbe6087..ba028ab 100644 --- a/cmd/web/unix.go +++ b/cmd/web/unix.go @@ -13,4 +13,3 @@ var DefaultLib string = filepath.Join(os.Getenv("HOME"), ".steam/steam/steamapps func startBrowser() { return } - diff --git a/cmd/web/windows.go b/cmd/web/windows.go index 034e347..f59761a 100644 --- a/cmd/web/windows.go +++ b/cmd/web/windows.go @@ -10,8 +10,7 @@ import ( var DefaultLib string = `C:\Program Files (x86)\Steam\steamapps` func startBrowser() { - time.Sleep(time.Second*3) + time.Sleep(time.Second * 3) c := exec.Command("cmd", "/c", "start", "http://127.0.0.1"+Listen) Logger.Printf("Running command: %s", c.Run()) } - -- cgit v1.2.3