aboutsummaryrefslogtreecommitdiff
path: root/cmd
diff options
context:
space:
mode:
Diffstat (limited to 'cmd')
-rw-r--r--cmd/web/app.go80
-rw-r--r--cmd/web/handlers.go102
-rw-r--r--cmd/web/install.go3
-rw-r--r--cmd/web/main.go19
-rw-r--r--cmd/web/routes.go4
-rw-r--r--cmd/web/templates/index.html26
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">