diff options
| author | Mitch Riedstra <mitch@riedstra.us> | 2021-08-04 23:53:36 -0400 |
|---|---|---|
| committer | Mitch Riedstra <mitch@riedstra.us> | 2021-08-04 23:53:36 -0400 |
| commit | c202f2eca32e1ab2e313417168351df1c58ee062 (patch) | |
| tree | 6540629b337d2d769581baec26096ac0555f71f9 /cmd | |
| parent | 742938b00222c7ad57ad11eb24850d9202c2503d (diff) | |
| download | steam-export-c202f2eca32e1ab2e313417168351df1c58ee062.tar.gz steam-export-c202f2eca32e1ab2e313417168351df1c58ee062.tar.xz | |
More major changes. Web UI works. Downloading games works. Status works. extractFile needs work
Diffstat (limited to 'cmd')
| -rw-r--r-- | cmd/web/app.go | 80 | ||||
| -rw-r--r-- | cmd/web/handlers.go | 102 | ||||
| -rw-r--r-- | cmd/web/install.go | 3 | ||||
| -rw-r--r-- | cmd/web/main.go | 19 | ||||
| -rw-r--r-- | cmd/web/routes.go | 4 | ||||
| -rw-r--r-- | cmd/web/templates/index.html | 26 |
6 files changed, 96 insertions, 138 deletions
diff --git a/cmd/web/app.go b/cmd/web/app.go index 9ab1c0e..578c44d 100644 --- a/cmd/web/app.go +++ b/cmd/web/app.go @@ -1,35 +1,38 @@ package main import ( - "html/template" - "sync" - "time" + "io/fs" + "os" "riedstra.dev/mitch/steam-export/steam" ) -// statusInfo represents the internal status of game installation -type statusInfo struct { - sync.RWMutex - Running bool - Error error - Url string - Transferred int64 - Size int64 - Start *time.Time -} +// // statusInfo represents the internal status of game installation +// type statusInfo struct { +// sync.RWMutex +// Running bool +// Error error +// Url string +// Transferred int64 +// Size int64 +// Start *time.Time +// } // App binds together the steam library, templates, and channel for install // requests as well as most the app specific http handlers. type App struct { - Library *steamLib - Status *statusInfo - Demo bool + Library *steam.Library + + // Whether or not we're running in demo mode + Demo bool + + ShareLink string + Version string - Templates *template.Template + templateFS fs.FS + staticFS fs.FS - // Sending to this channel triggers the downloader in the background - download chan string + // download chan string } // NewApp sets up the steam library for us as well as parses the embedded @@ -41,39 +44,18 @@ func NewApp(libPath string) (*App, error) { } a := &App{ - Library: &steamLib{}, - Status: &statusInfo{}, - download: make(chan string), + Library: lib, + Version: Version, + ShareLink: getShareLink(), + // download: make(chan string), + templateFS: TemplateFS, + staticFS: StaticFS, } - a.Library.Library = *lib - - a.Templates = template.Must(template.New("index").Parse(indexTemplate)) - return a, nil } -// LibrarySet takes care of locking the Library and switching over to a new -// path, unlocking when done. -// Errors will be logged and no changes will be made if unsuccessful. -func (a *App) LibrarySet(path string) { - Logger.Println("Starting library reload") - a.Library.Lock() - defer a.Library.Unlock() - var err error - l2, err := steam.NewLibrary(path) - if err != nil { - Logger.Printf("Error reopening lib: %s", err) - return - } - a.Library.Library = *l2 - Logger.Println("Done reloading lib") -} - -// LibraryReload calls LibrarySet but with the current directory, forcing a -// reload of information off of disk. -func (a *App) LibraryReload() { - cur := a.Library.Folder - a.LibrarySet(cur) - return +func (a *App) useLocalFS(pth string) { + a.templateFS = os.DirFS(pth) + a.staticFS = a.templateFS } diff --git a/cmd/web/handlers.go b/cmd/web/handlers.go index 4b86b58..fca7471 100644 --- a/cmd/web/handlers.go +++ b/cmd/web/handlers.go @@ -3,7 +3,7 @@ package main import ( "encoding/json" "fmt" - "io" + "html/template" "net/http" "net/url" "os" @@ -11,36 +11,26 @@ import ( "time" "github.com/gorilla/mux" - "riedstra.dev/mitch/steam-export/steam" ) // HandleIndex takes care of rendering our embedded template // and locks the steam library for each 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 - a.Library.Lock() - defer a.Library.Unlock() - a.Status.Lock() - defer a.Status.Unlock() - - err := a.Templates.ExecuteTemplate(w, "index", + t, err := template.ParseFS(a.templateFS, "templates/index.html") + if err != nil { + Logger.Printf("While parsing template for index: %s", err) + http.Error(w, "Internal server error ( template )", + http.StatusInternalServerError) + return + } + + err = t.Execute(w, struct { - Lib *steam.Library - Info *statusInfo - Local bool - ShareLink string - Version string - Demo bool + App *App + Local bool }{ - &a.Library.Library, - a.Status, + a, isLocal(r.RemoteAddr), - getShareLink(), - Version, - a.Demo, }) if err != nil { Logger.Printf("While Rendering template: %s", err) @@ -60,6 +50,7 @@ func (a *App) HandleInstall(w http.ResponseWriter, r *http.Request) { uri := r.Form.Get("uri") + // Sanity checking on our end before we pass it off if strings.HasPrefix(uri, "http") { _, err := url.Parse(uri) if err != nil { @@ -77,7 +68,14 @@ func (a *App) HandleInstall(w http.ResponseWriter, r *http.Request) { } Logger.Printf("Installer: Sending request for: %s to channel", uri) - a.download <- uri + + go func() { + g, err := a.Library.ExtractSmart(uri) + if err != nil { + Logger.Printf("Error encountered installing: %s", err) + } + Logger.Printf("Extrated game: %s", g) + }() http.Redirect(w, r, "/", 302) } @@ -87,9 +85,7 @@ func (a *App) HandleDownload(w http.ResponseWriter, r *http.Request) { vars := mux.Vars(r) game := vars["game"] - a.Library.Lock() - g, ok := a.Library.Games[game] - a.Library.Unlock() + g, ok := a.Library.Games()[game] if !ok { Logger.Printf("Missing: %s", game) http.Error(w, "Game is missing", 404) @@ -101,37 +97,11 @@ func (a *App) HandleDownload(w http.ResponseWriter, r *http.Request) { Logger.Printf("Client %s is downloading: %s", r.RemoteAddr, game) - // Invert the writer so we can break up the copy and get progress - // information in here - rdr, pwrtr := io.Pipe() - go func() { - err := g.Package(pwrtr) - if err != nil { - Logger.Println("Error in package writing: ", err) - } - }() - - var total int64 - start := time.Now() - for { - n, err := io.CopyN(w, rdr, 256*1024*1024) - if err == io.EOF { - break - } - if err != nil { - Logger.Printf("Client %s Error Sending game: %s", r.RemoteAddr, err) - // Headers already sent, don't bother sending an error - return - } - total += n - mb := float64(total / 1024 / 1024) - rate := mb / time.Since(start).Seconds() - - Logger.Printf("Client %s is downloading %s: %0.1f%% done %.2f mb/s", - r.RemoteAddr, game, float64(total)/float64(g.Size)*100, rate) + err := a.Library.Package(g.Name, w) + if err != nil { + Logger.Printf("Encountered download error: %s", err) } - - Logger.Printf("Client %s finished downloading: %s", r.RemoteAddr, game) + return } // HandleDelete removes the game in question, though it doesn't @@ -153,16 +123,14 @@ func (a *App) HandleDelete(w http.ResponseWriter, r *http.Request) { return } - a.Library.Lock() - g, ok := a.Library.Games[game] - a.Library.Unlock() + g, ok := a.Library.Games()[game] if !ok { Logger.Printf("Missing: %s", game) http.Error(w, "Game is missing", 404) return } - err = g.Delete() + err = a.Library.Delete(g.Name) if err != nil { Logger.Printf("Error removing game: %s", err) http.Error(w, fmt.Sprintf("Error removing game: %s", err), 500) @@ -170,21 +138,19 @@ func (a *App) HandleDelete(w http.ResponseWriter, r *http.Request) { } Logger.Printf("Removed game: %s", game) - a.LibraryReload() http.Redirect(w, r, "/", 302) } // HandleStats dumps out some internal statistics of installation which // is then parsed by some JS for a progress bar and such 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(a.Status) + enc.SetIndent("", " ") + + err := enc.Encode(a.Library.Status()) if err != nil { Logger.Println("While encoding Status: ", err) } @@ -200,7 +166,7 @@ func (a *App) HandleSetLib(w http.ResponseWriter, r *http.Request) { return } - a.LibrarySet(r.Form.Get("path")) + a.Library.ProcessLibrary(r.Form.Get("path")) http.Redirect(w, r, "/", 302) } @@ -211,7 +177,7 @@ func HandleQuit(w http.ResponseWriter, r *http.Request) { w.Header().Add("Content-type", "text/plain") w.Write([]byte("Shutting down... feel free to close this")) go func() { - time.Sleep(time.Second * 2) + time.Sleep(time.Millisecond * 50) os.Exit(0) }() return diff --git a/cmd/web/install.go b/cmd/web/install.go index e59a5c5..2645797 100644 --- a/cmd/web/install.go +++ b/cmd/web/install.go @@ -1,5 +1,7 @@ package main +/* + import ( "fmt" "io" @@ -111,3 +113,4 @@ func (a *App) installer() { a.LibraryReload() } } +*/ diff --git a/cmd/web/main.go b/cmd/web/main.go index 045f60a..20c3506 100644 --- a/cmd/web/main.go +++ b/cmd/web/main.go @@ -21,9 +21,10 @@ var ( Listen = ":8899" //go:embed static/* - embeddedStatic embed.FS - //go:embed templates/index.html - indexTemplate string + StaticFS embed.FS + //go:embed templates/* + TemplateFS embed.FS + //indexTemplate string ) func main() { @@ -36,6 +37,9 @@ func main() { fl.StringVar(&shareLink, "s", shareLink, "Share link, if blank make an educated guess") isDemo := fl.Bool("demo", false, "Whether or not to run in demo mode. You probably don't want this on.") + localFS := fl.String("fs", "", + "If not empty the local path to use instead of the "+ + "embedded templates and /static directory.") fl.Parse(os.Args[1:]) if *debug { @@ -47,12 +51,14 @@ func main() { Logger.Fatal(err) } + if *localFS != "" { + a.useLocalFS(*localFS) + } + if *isDemo { a.Demo = true } - go a.installer() - s := http.Server{Handler: a} for i := 0; i < 5; i++ { @@ -74,7 +80,8 @@ func main() { continue } - startBrowser("http://localhost" + Listen) + // Not using 'localhost' due to the way windows listens by default + startBrowser("http://127.0.0.1" + Listen) err = s.Serve(l) if err != nil { diff --git a/cmd/web/routes.go b/cmd/web/routes.go index 6d0e529..12bb807 100644 --- a/cmd/web/routes.go +++ b/cmd/web/routes.go @@ -9,7 +9,7 @@ import ( func (a *App) ServeHTTP(w http.ResponseWriter, r *http.Request) { rtr := mux.NewRouter() - rtr.PathPrefix("/api/v1").Handler(a.HandleAPIv1()) + // rtr.PathPrefix("/api/v1").Handler(a.HandleAPIv1()) rtr.Handle("/quit", UnauthorizedIfNotLocal(http.HandlerFunc(HandleQuit))) rtr.Handle("/setLib", UnauthorizedIfNotLocal(http.HandlerFunc(a.HandleSetLib))) @@ -20,7 +20,7 @@ func (a *App) ServeHTTP(w http.ResponseWriter, r *http.Request) { rtr.HandleFunc("/steam-export-web.exe", ServeSelf) rtr.HandleFunc("/download/{game}", a.HandleDownload) rtr.PathPrefix("/static").Handler( - http.FileServer(http.FS(embeddedStatic))) + http.FileServer(http.FS(a.staticFS))) rtr.HandleFunc("/", a.HandleIndex) rtr.ServeHTTP(w, r) diff --git a/cmd/web/templates/index.html b/cmd/web/templates/index.html index 0717005..9f8c4b0 100644 --- a/cmd/web/templates/index.html +++ b/cmd/web/templates/index.html @@ -26,7 +26,7 @@ <div class="collapse navbar-collapse" id="navbarsExampleDefault"> <ul class="navbar-nav me-auto mb-2 mb-md-0"> <li class="nav-item"> - {{ if .Demo }} + {{ if .App.Demo }} <span class="d-inline-block" tabindex="0" data-bs-toggle="tooltip" data-bs-placement="bottom" title="Disabled for demo"> <a class="nav-link disabled" aria-current="page" href="#"> @@ -43,7 +43,7 @@ <li class="nav-item"> <a href="#" id="shareLink" - data-clipboard-text="{{$.ShareLink}}" + data-clipboard-text="{{$.App.ShareLink}}" class="nav-link"> Copy Share link </a> @@ -78,7 +78,7 @@ Version </a> </li> - {{if .Demo}}{{else}} + {{if .App.Demo}}{{else}} <li class="nav-item"> <a class="nav-link" href="/quit">Shutdown Server/Quit</a> </li> @@ -99,7 +99,7 @@ {{ if .Local }} <script src="/static/main.js"></script> - <h2>Library: {{.Lib.Folder}}</h2> + <h2>Library: {{.App.Library.Folder}}</h2> <div class="row"> <div id="installBarContainer" class="progress" style="display: none;"> @@ -125,11 +125,11 @@ <p> It also allows you to import games from across the network as well if you provide an HTTP url from which to download the game file as exported - from this application. {{ if .Demo }}Downloads are however disabled for the + from this application. {{ if .App.Demo }}Downloads are however disabled for the demo.{{end}} </p> <p> - {{ if .Demo }} + {{ if .App.Demo }} You would normally be able to download the application from here, but it's disabled for the demo. See <a href="https://git.riedstra.dev/mitch/steam-export/about/">here</a> for more info. @@ -144,14 +144,14 @@ You can give people this link to view the library remotely and download games from your computer: <br /><br /> - <a href="{{.ShareLink}}">{{.ShareLink}}</a> + <a href="{{.App.ShareLink}}">{{.App.ShareLink}}</a> </p> {{ else }} <h2>Remote Steam library access</h2> <p> - {{ if .Demo }}{{ else }} + {{ if .App.Demo }}{{ else }} <a href="/steam-export-web.exe"> If you need this program to install the games click here. </a> @@ -181,13 +181,13 @@ </tr> </thead> <tbody> - {{ range $key, $val := .Lib.Games}} + {{ range $key, $val := .App.Library.Games}} <tr> <td>{{$key}}</td> <td>{{$val.GetSize}}</td> <td> - {{if $.Demo}} + {{if $.App.Demo}} <span class="d-inline-block" tabindex="0" data-bs-toggle="tooltip" data-bs-placement="left" title="Disabled for demo"> <a href="#" class="btn btn-secondary disabled">Download</a> @@ -195,7 +195,7 @@ {{ else }} <a href="/download/{{$key}}" class="btn btn-secondary">Download</a> {{ end }} - <button data-clipboard-text="{{$.ShareLink}}download/{{$key}}" class="btn btn-primary">Copy link</button> + <button data-clipboard-text="{{$.App.ShareLink}}download/{{$key}}" class="btn btn-primary">Copy link</button> {{ if $.Local }} <button data-bs-target="#delete{{$val.Slug}}Modal" data-bs-toggle="modal" class="btn btn-danger">Delete</button> {{ end }} @@ -209,7 +209,7 @@ {{ if .Local }} -{{ range $key, $val := .Lib.Games }} +{{ range $key, $val := .App.Library.Games }} <div class="modal fade" id="delete{{$val.Slug}}Modal" tabindex="-1" aria-labelledby="delete{{$val.Slug}}ModalLabel" aria-hidden="true"> <div class="modal-dialog modal-dialog-centered modal-dialog-scrollable"> @@ -371,7 +371,7 @@ </div> <div class="modal-body"> - <pre><code>{{.Version}}</pre></code> + <pre><code>{{.App.Version}}</pre></code> </div> <div class="modal-footer"> |
