From 4c904e7196fdbd62f7f03b6fcd20865b2c197b1b Mon Sep 17 00:00:00 2001 From: Steven Polley Date: Mon, 13 Nov 2023 18:05:20 -0700 Subject: [PATCH] add web client - future plans for loading screen during refresh --- main.go | 20 +++++++++++++++++--- public/README.md | 3 +++ templates/README.md | 3 +++ templates/home.html | 22 ++++++++++++++++++++++ webServer.go | 23 +++++++++++++++++++---- 5 files changed, 64 insertions(+), 7 deletions(-) create mode 100644 public/README.md create mode 100644 templates/README.md create mode 100644 templates/home.html diff --git a/main.go b/main.go index 9ac3d68..f6aae4c 100644 --- a/main.go +++ b/main.go @@ -3,15 +3,19 @@ package main import ( "log" "os" + "sync" + "text/template" "time" "deadbeef.codes/steven/ynab-portfolio-monitor/ynab" ) var ( - configuredProviders []AccountProvider // Any account providers that are successfully configured get added to this slice - ynabClient *ynab.Client // YNAB HTTP client - lastRefresh time.Time + configuredProviders []AccountProvider // Any account providers that are successfully configured get added to this slice + ynabClient *ynab.Client // YNAB HTTP client + lastRefresh time.Time // Timestamp that last data refresh ran - used for rate limiting + refreshRunning *sync.Mutex // True if a refresh is currently running + t *template.Template // HTML templates in the templates directory ) // Called at program startup or if SIGHUP is received @@ -49,6 +53,13 @@ func init() { if err != nil { log.Fatalf("failed to create ynab client: %v", err) } + + // Web Templates + // Parse all template files at startup + t, err = template.ParseGlob("./templates/*") + if err != nil { + log.Fatalf("couldn't parse HTML templates: %v", err) + } } func main() { @@ -62,10 +73,13 @@ func main() { } func refreshData() { + refreshRunning.Lock() + defer refreshRunning.Unlock() // Only allow a refresh at most once every 5 minutes if time.Now().Before(lastRefresh.Add(time.Minute * 5)) { log.Printf("refresh rate limited") + return } lastRefresh = time.Now() diff --git a/public/README.md b/public/README.md new file mode 100644 index 0000000..e36f855 --- /dev/null +++ b/public/README.md @@ -0,0 +1,3 @@ +# Public + +This directory is publicly accessible from the HTTP server and contains web assets such as images, css and javascript. \ No newline at end of file diff --git a/templates/README.md b/templates/README.md new file mode 100644 index 0000000..87545aa --- /dev/null +++ b/templates/README.md @@ -0,0 +1,3 @@ +# Templates + +This directory contains HTML templates used by the Go templating engine. \ No newline at end of file diff --git a/templates/home.html b/templates/home.html new file mode 100644 index 0000000..04d8c63 --- /dev/null +++ b/templates/home.html @@ -0,0 +1,22 @@ + + + YNAB Portfolio Monitor + + + +

Loading...

+ + \ No newline at end of file diff --git a/webServer.go b/webServer.go index 01c1bac..55c89a7 100644 --- a/webServer.go +++ b/webServer.go @@ -1,13 +1,11 @@ package main import ( - "fmt" "log" "net/http" ) func webServer() { - // Start web server //Public static files http.Handle("/public/", http.StripPrefix("/public/", http.FileServer(http.Dir("public")))) @@ -15,13 +13,30 @@ func webServer() { // Page Handlers // Anything that is responsible for the base elements of a viewable web page http.HandleFunc("/", homePageHandler) + http.HandleFunc("/status", statusHandler) + // Start web server log.Print("Service listening on :8080") log.Printf("Web server unexpectedly exiting!: %v", http.ListenAndServe(":8080", nil)) +} +type homePageData struct { + BudgetID string } func homePageHandler(w http.ResponseWriter, r *http.Request) { - refreshData() - http.Redirect(w, r, fmt.Sprintf("https://app.ynab.com/%s", ynabClient.BudgetID), http.StatusSeeOther) + go refreshData() + pageData := &homePageData{BudgetID: ynabClient.BudgetID} + err := t.ExecuteTemplate(w, "home.html", pageData) + if err != nil { + log.Printf("error executing home.html template: %v", err) + } + + //http.Redirect(w, r, fmt.Sprintf("https://app.ynab.com/%s", ynabClient.BudgetID), http.StatusSeeOther) +} + +func statusHandler(w http.ResponseWriter, r *http.Request) { + refreshRunning.Lock() + w.WriteHeader(http.StatusOK) + refreshRunning.Unlock() }